mirror of https://github.com/M66B/FairEmail.git
parent
98bd662201
commit
9c4a81bda9
@ -1,443 +0,0 @@
|
||||
package biweekly;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import biweekly.io.chain.ChainingJsonParser;
|
||||
import biweekly.io.chain.ChainingJsonStringParser;
|
||||
import biweekly.io.chain.ChainingJsonWriter;
|
||||
import biweekly.io.chain.ChainingTextParser;
|
||||
import biweekly.io.chain.ChainingTextStringParser;
|
||||
import biweekly.io.chain.ChainingTextWriter;
|
||||
import biweekly.io.chain.ChainingXmlMemoryParser;
|
||||
import biweekly.io.chain.ChainingXmlParser;
|
||||
import biweekly.io.chain.ChainingXmlWriter;
|
||||
import biweekly.io.json.JCalReader;
|
||||
import biweekly.io.json.JCalWriter;
|
||||
import biweekly.io.text.ICalReader;
|
||||
import biweekly.io.text.ICalWriter;
|
||||
import biweekly.io.xml.XCalDocument;
|
||||
import biweekly.util.IOUtils;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Contains static chaining factory methods for reading/writing iCalendar
|
||||
* objects.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <b>Writing an iCalendar object</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* ICalendar ical = new ICalendar();
|
||||
*
|
||||
* //string
|
||||
* String icalString = Biweekly.write(ical).go();
|
||||
*
|
||||
* //file
|
||||
* File file = new File("meeting.ics");
|
||||
* Biweekly.write(ical).go(file);
|
||||
*
|
||||
* //output stream
|
||||
* OutputStream out = ...
|
||||
* Biweekly.write(ical).go(out);
|
||||
* out.close();
|
||||
*
|
||||
* //writer (should be configured to use UTF-8 encoding)
|
||||
* Writer writer = ...
|
||||
* Biweekly.write(ical).go(writer);
|
||||
* writer.close();
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* <b>Writing multiple iCalendar objects</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* ICalendar ical1 = new ICalendar();
|
||||
* ICalendar ical2 = new ICalendar();
|
||||
*
|
||||
* String icalString = Biweekly.write(ical1, ical2).go();
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* <b>Writing an XML-encoded iCalendar object (xCal)</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* //Call writeXml() instead of write()
|
||||
* ICalendar ical = new ICalendar();
|
||||
* String xml = Biweekly.writeXml(ical).indent(2).go();
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* <b>Writing a JSON-encoded iCalendar object (jCal)</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* //Call writeJson() instead of write()
|
||||
* ICalendar ical = new ICalendar();
|
||||
* String json = Biweekly.writeJson(ical).go();
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* <b>Reading an iCalendar object</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* ICalendar ical;
|
||||
*
|
||||
* //string
|
||||
* String icalStr = ...
|
||||
* ical = Biweekly.parse(icalStr).first();
|
||||
*
|
||||
* //file
|
||||
* File file = new File("meeting.ics");
|
||||
* ical = Biweekly.parse(file).first();
|
||||
*
|
||||
* //input stream
|
||||
* InputStream in = ...
|
||||
* ical = Biweekly.parse(in).first();
|
||||
* in.close();
|
||||
*
|
||||
* //reader (should be configured to read UTF-8)
|
||||
* Reader reader = ...
|
||||
* ical = Biweekly.parse(reader).first();
|
||||
* reader.close();
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* <b>Reading multiple iCalendar objects</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* String icalStr = ...
|
||||
* List<ICalendar> icals = Biweekly.parse(icalStr).all();
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* <b>Reading an XML-encoded iCalendar object (xCal)</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* //Call parseXml() instead of parse()
|
||||
* String xml = ...
|
||||
* ICalendar ical = Biweekly.parseXml(xml).first();
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* <b>Reading a JSON-encoded iCalendar object (Cal)</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* //Call parseJson() instead of parse()
|
||||
* String json = ...
|
||||
* ICalendar ical = Biweekly.parseJson(json).first();
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* <b>Retrieving parser warnings</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* String icalStr = ...
|
||||
* List<List<String>> warnings = new ArrayList<List<String>>();
|
||||
* List<ICalendar> icals = Biweekly.parse(icalStr).warnings(warnings).all();
|
||||
* int i = 0;
|
||||
* for (List<String> icalWarnings : warnings) {
|
||||
* System.out.println("iCal #" + (i++) + " warnings:");
|
||||
* for (String warning : icalWarnings) {
|
||||
* System.out.println(warning);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* The methods in this class make use of the following classes. These classes
|
||||
* can be used if greater control over the read/write operation is required:
|
||||
* </p>
|
||||
*
|
||||
* <table class="simpleTable">
|
||||
* <caption>Classes used by this class</caption>
|
||||
* <tr>
|
||||
* <th></th>
|
||||
* <th>Classes</th>
|
||||
* <th>Supports<br>
|
||||
* streaming?</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <th>Text</th>
|
||||
* <td>{@link ICalReader} / {@link ICalWriter}</td>
|
||||
* <td>yes</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <th>XML</th>
|
||||
* <td>{@link XCalDocument}</td>
|
||||
* <td>no</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <th>JSON</th>
|
||||
* <td>{@link JCalReader} / {@link JCalWriter}</td>
|
||||
* <td>yes</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public final class Biweekly {
|
||||
/**
|
||||
* The version of the library.
|
||||
*/
|
||||
public static final String VERSION;
|
||||
|
||||
/**
|
||||
* The Maven group ID.
|
||||
*/
|
||||
public static final String GROUP_ID;
|
||||
|
||||
/**
|
||||
* The Maven artifact ID.
|
||||
*/
|
||||
public static final String ARTIFACT_ID;
|
||||
|
||||
/**
|
||||
* The project webpage.
|
||||
*/
|
||||
public static final String URL;
|
||||
|
||||
static {
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = Biweekly.class.getResourceAsStream("/biweekly/biweekly.properties");
|
||||
Properties props = new Properties();
|
||||
props.load(in);
|
||||
|
||||
VERSION = props.getProperty("version");
|
||||
GROUP_ID = props.getProperty("groupId");
|
||||
ARTIFACT_ID = props.getProperty("artifactId");
|
||||
URL = props.getProperty("url");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an iCalendar object string.
|
||||
* @param ical the iCalendar data
|
||||
* @return chainer object for completing the parse operation
|
||||
*/
|
||||
public static ChainingTextStringParser parse(String ical) {
|
||||
return new ChainingTextStringParser(ical);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an iCalendar file.
|
||||
* @param file the iCalendar file
|
||||
* @return chainer object for completing the parse operation
|
||||
*/
|
||||
public static ChainingTextParser<ChainingTextParser<?>> parse(File file) {
|
||||
return new ChainingTextParser<ChainingTextParser<?>>(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an iCalendar data stream.
|
||||
* @param in the input stream
|
||||
* @return chainer object for completing the parse operation
|
||||
*/
|
||||
public static ChainingTextParser<ChainingTextParser<?>> parse(InputStream in) {
|
||||
return new ChainingTextParser<ChainingTextParser<?>>(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an iCalendar data stream.
|
||||
* @param reader the reader
|
||||
* @return chainer object for completing the parse operation
|
||||
*/
|
||||
public static ChainingTextParser<ChainingTextParser<?>> parse(Reader reader) {
|
||||
return new ChainingTextParser<ChainingTextParser<?>>(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes multiple iCalendar objects to a data stream.
|
||||
* @param icals the iCalendar objects to write
|
||||
* @return chainer object for completing the write operation
|
||||
*/
|
||||
public static ChainingTextWriter write(ICalendar... icals) {
|
||||
return write(Arrays.asList(icals));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes multiple iCalendar objects to a data stream.
|
||||
* @param icals the iCalendar objects to write
|
||||
* @return chainer object for completing the write operation
|
||||
*/
|
||||
public static ChainingTextWriter write(Collection<ICalendar> icals) {
|
||||
return new ChainingTextWriter(icals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an xCal document (XML-encoded iCalendar objects) from a string.
|
||||
* @param xml the XML string
|
||||
* @return chainer object for completing the parse operation
|
||||
*/
|
||||
public static ChainingXmlMemoryParser parseXml(String xml) {
|
||||
return new ChainingXmlMemoryParser(xml);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an xCal document (XML-encoded iCalendar objects) from a file.
|
||||
* @param file the XML file
|
||||
* @return chainer object for completing the parse operation
|
||||
*/
|
||||
public static ChainingXmlParser<ChainingXmlParser<?>> parseXml(File file) {
|
||||
return new ChainingXmlParser<ChainingXmlParser<?>>(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an xCal document (XML-encoded iCalendar objects) from an input
|
||||
* stream.
|
||||
* @param in the input stream
|
||||
* @return chainer object for completing the parse operation
|
||||
*/
|
||||
public static ChainingXmlParser<ChainingXmlParser<?>> parseXml(InputStream in) {
|
||||
return new ChainingXmlParser<ChainingXmlParser<?>>(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Parses an xCal document (XML-encoded iCalendar objects) from a reader.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that use of this method is discouraged. It ignores the character
|
||||
* encoding that is defined within the XML document itself, and should only
|
||||
* be used if the encoding is undefined or if the encoding needs to be
|
||||
* ignored for whatever reason. The {@link #parseXml(InputStream)} method
|
||||
* should be used instead, since it takes the XML document's character
|
||||
* encoding into account when parsing.
|
||||
* </p>
|
||||
* @param reader the reader
|
||||
* @return chainer object for completing the parse operation
|
||||
*/
|
||||
public static ChainingXmlParser<ChainingXmlParser<?>> parseXml(Reader reader) {
|
||||
return new ChainingXmlParser<ChainingXmlParser<?>>(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an xCal document (XML-encoded iCalendar objects).
|
||||
* @param document the XML document
|
||||
* @return chainer object for completing the parse operation
|
||||
*/
|
||||
public static ChainingXmlMemoryParser parseXml(Document document) {
|
||||
return new ChainingXmlMemoryParser(document);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an xCal document (XML-encoded iCalendar objects).
|
||||
* @param icals the iCalendar object(s) to write
|
||||
* @return chainer object for completing the write operation
|
||||
*/
|
||||
public static ChainingXmlWriter writeXml(ICalendar... icals) {
|
||||
return writeXml(Arrays.asList(icals));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an xCal document (XML-encoded iCalendar objects).
|
||||
* @param icals the iCalendar objects to write
|
||||
* @return chainer object for completing the write operation
|
||||
*/
|
||||
public static ChainingXmlWriter writeXml(Collection<ICalendar> icals) {
|
||||
return new ChainingXmlWriter(icals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a jCal data stream (JSON-encoded iCalendar objects).
|
||||
* @param json the JSON data
|
||||
* @return chainer object for completing the parse operation
|
||||
*/
|
||||
public static ChainingJsonStringParser parseJson(String json) {
|
||||
return new ChainingJsonStringParser(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a jCal data stream (JSON-encoded iCalendar objects).
|
||||
* @param file the JSON file
|
||||
* @return chainer object for completing the parse operation
|
||||
*/
|
||||
public static ChainingJsonParser<ChainingJsonParser<?>> parseJson(File file) {
|
||||
return new ChainingJsonParser<ChainingJsonParser<?>>(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a jCal data stream (JSON-encoded iCalendar objects).
|
||||
* @param in the input stream
|
||||
* @return chainer object for completing the parse operation
|
||||
*/
|
||||
public static ChainingJsonParser<ChainingJsonParser<?>> parseJson(InputStream in) {
|
||||
return new ChainingJsonParser<ChainingJsonParser<?>>(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a jCal data stream (JSON-encoded iCalendar objects).
|
||||
* @param reader the reader
|
||||
* @return chainer object for completing the parse operation
|
||||
*/
|
||||
public static ChainingJsonParser<ChainingJsonParser<?>> parseJson(Reader reader) {
|
||||
return new ChainingJsonParser<ChainingJsonParser<?>>(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an xCal document (XML-encoded iCalendar objects).
|
||||
* @param icals the iCalendar object(s) to write
|
||||
* @return chainer object for completing the write operation
|
||||
*/
|
||||
public static ChainingJsonWriter writeJson(ICalendar... icals) {
|
||||
return writeJson(Arrays.asList(icals));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an xCal document (XML-encoded iCalendar objects).
|
||||
* @param icals the iCalendar objects to write
|
||||
* @return chainer object for completing the write operation
|
||||
*/
|
||||
public static ChainingJsonWriter writeJson(Collection<ICalendar> icals) {
|
||||
return new ChainingJsonWriter(icals);
|
||||
}
|
||||
|
||||
private Biweekly() {
|
||||
//hide
|
||||
}
|
||||
}
|
@ -1,222 +0,0 @@
|
||||
package biweekly;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import biweekly.util.CaseClasses;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines the data type of a property's value.
|
||||
* @author Michael Angstadt
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-29">RFC 5545
|
||||
* p.29-50</a>
|
||||
*/
|
||||
public class ICalDataType {
|
||||
private static final CaseClasses<ICalDataType, String> enums = new CaseClasses<ICalDataType, String>(ICalDataType.class) {
|
||||
@Override
|
||||
protected ICalDataType create(String value) {
|
||||
return new ICalDataType(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(ICalDataType dataType, String value) {
|
||||
return dataType.name.equalsIgnoreCase(value);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Binary data (such as an image or word-processing document).
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-30">RFC 5545
|
||||
* p.30-1</a>
|
||||
* @see <a href="http://www.imc.org/pdi/vcal-10.doc">vCal 1.0 p.18</a>
|
||||
*/
|
||||
public static final ICalDataType BINARY = new ICalDataType("BINARY");
|
||||
|
||||
/**
|
||||
* Boolean value ("true" or "false").
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-31">RFC 5545
|
||||
* p.31</a>
|
||||
*/
|
||||
public static final ICalDataType BOOLEAN = new ICalDataType("BOOLEAN");
|
||||
|
||||
/**
|
||||
* A URI containing a calendar user address (typically, a "mailto" URI).
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-30">RFC 5545
|
||||
* p.30-1</a>
|
||||
*/
|
||||
public static final ICalDataType CAL_ADDRESS = new ICalDataType("CAL-ADDRESS");
|
||||
|
||||
/**
|
||||
* The property value is located in a separate MIME entity (vCal 1.0 only).
|
||||
* @see <a href="http://www.imc.org/pdi/vcal-10.doc">vCal 1.0 p.17</a>
|
||||
*/
|
||||
public static final ICalDataType CONTENT_ID = new ICalDataType("CONTENT-ID"); //1.0 only
|
||||
|
||||
/**
|
||||
* A date (for example, "2014-03-12").
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-32">RFC 5545
|
||||
* p.32</a>
|
||||
* @see <a href="http://www.imc.org/pdi/vcal-10.doc">vCal 1.0 p.16-7</a>
|
||||
*/
|
||||
public static final ICalDataType DATE = new ICalDataType("DATE");
|
||||
|
||||
/**
|
||||
* A date/time value (for example, "2014-03-12 13:30:00").
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-32">RFC 5545
|
||||
* p.32-4</a>
|
||||
* @see <a href="http://www.imc.org/pdi/vcal-10.doc">vCal 1.0 p.16-7</a>
|
||||
*/
|
||||
public static final ICalDataType DATE_TIME = new ICalDataType("DATE-TIME");
|
||||
|
||||
/**
|
||||
* A duration of time (for example, "2 hours, 30 minutes").
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-35">RFC 5545
|
||||
* p.35-6</a>
|
||||
* @see <a href="http://www.imc.org/pdi/vcal-10.doc">vCal 1.0 p.17</a>
|
||||
*/
|
||||
public static final ICalDataType DURATION = new ICalDataType("DURATION");
|
||||
|
||||
/**
|
||||
* A floating point value (for example, "3.14")
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-36">RFC 5545
|
||||
* p.36</a>
|
||||
*/
|
||||
public static final ICalDataType FLOAT = new ICalDataType("FLOAT");
|
||||
|
||||
/**
|
||||
* An integer value (for example, "42")
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-37">RFC 5545
|
||||
* p.37</a>
|
||||
*/
|
||||
public static final ICalDataType INTEGER = new ICalDataType("INTEGER");
|
||||
|
||||
/**
|
||||
* A period of time (for example, "October 3 through October 5").
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-37-8">RFC 5545
|
||||
* p.37-8</a>
|
||||
*/
|
||||
public static final ICalDataType PERIOD = new ICalDataType("PERIOD");
|
||||
|
||||
/**
|
||||
* A recurrence rule (for example, "every Monday at 2pm").
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-38">RFC 5545
|
||||
* p.38-45</a>
|
||||
* @see <a href="http://www.imc.org/pdi/vcal-10.doc">vCal 1.0 p.18-23</a>
|
||||
*/
|
||||
public static final ICalDataType RECUR = new ICalDataType("RECUR");
|
||||
|
||||
/**
|
||||
* A plain text value.
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-45">RFC 5545
|
||||
* p.45-6</a>
|
||||
*/
|
||||
public static final ICalDataType TEXT = new ICalDataType("TEXT");
|
||||
|
||||
/**
|
||||
* A time value (for example, "2pm").
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-47">RFC 5545
|
||||
* p.47-8</a>
|
||||
*/
|
||||
public static final ICalDataType TIME = new ICalDataType("TIME");
|
||||
|
||||
/**
|
||||
* A URI value.
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-49">RFC 5545
|
||||
* p.49</a>
|
||||
*/
|
||||
public static final ICalDataType URI = new ICalDataType("URI");
|
||||
|
||||
/**
|
||||
* A URL (for example, "http://example.com/picture.jpg") (vCal 1.0 only).
|
||||
* @see <a href="http://www.imc.org/pdi/vcal-10.doc">vCal 1.0 p.17-8</a>
|
||||
*/
|
||||
public static final ICalDataType URL = new ICalDataType("URL");
|
||||
|
||||
/**
|
||||
* A UTC-offset (for example, "+0500").
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-49">RFC 5545
|
||||
* p.49-50</a>
|
||||
*/
|
||||
public static final ICalDataType UTC_OFFSET = new ICalDataType("UTC-OFFSET");
|
||||
|
||||
private final String name;
|
||||
|
||||
private ICalDataType(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the data type.
|
||||
* @return the name of the data type (e.g. "TEXT")
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a parameter value that is defined as a static constant in
|
||||
* this class.
|
||||
* @param value the parameter value
|
||||
* @return the object or null if not found
|
||||
*/
|
||||
public static ICalDataType find(String value) {
|
||||
if ("CID".equalsIgnoreCase(value)) {
|
||||
//"CID" is an alias for "CONTENT-ID" (vCal 1.0, p.17)
|
||||
return CONTENT_ID;
|
||||
}
|
||||
return enums.find(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a parameter value and creates one if it cannot be found. All
|
||||
* objects are guaranteed to be unique, so they can be compared with
|
||||
* {@code ==} equality.
|
||||
* @param value the parameter value
|
||||
* @return the object
|
||||
*/
|
||||
public static ICalDataType get(String value) {
|
||||
if ("CID".equalsIgnoreCase(value)) {
|
||||
//"CID" is an alias for "CONTENT-ID" (vCal 1.0, p.17)
|
||||
return CONTENT_ID;
|
||||
}
|
||||
return enums.get(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all of the parameter values that are defined as static constants in
|
||||
* this class.
|
||||
* @return the parameter values
|
||||
*/
|
||||
public static Collection<ICalDataType> all() {
|
||||
return enums.all();
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
package biweekly;
|
||||
|
||||
import com.github.mangstadt.vinnie.SyntaxStyle;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines all supported versions of the iCalendar standard.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public enum ICalVersion {
|
||||
/**
|
||||
* The original vCalendar specification.
|
||||
* @see <a href="http://www.imc.org/pdi/pdiproddev.html">1.0 specs</a>
|
||||
*/
|
||||
V1_0("1.0", SyntaxStyle.OLD),
|
||||
|
||||
/**
|
||||
* An older, deprecated version of the iCalendar specification (very similar
|
||||
* to {@link #V2_0}).
|
||||
* @see <a href="https://tools.ietf.org/html/rfc2445">RFC 2445</a>
|
||||
*/
|
||||
V2_0_DEPRECATED("2.0", SyntaxStyle.NEW),
|
||||
|
||||
/**
|
||||
* The latest iCalendar specification.
|
||||
* @see <a href="https://tools.ietf.org/html/rfc5545">RFC 5545</a>
|
||||
*/
|
||||
V2_0("2.0", SyntaxStyle.NEW);
|
||||
|
||||
private final String version;
|
||||
private final SyntaxStyle syntaxStyle;
|
||||
|
||||
/**
|
||||
* @param version the version number
|
||||
*/
|
||||
ICalVersion(String version, SyntaxStyle syntaxStyle) {
|
||||
this.version = version;
|
||||
this.syntaxStyle = syntaxStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text representation of this version.
|
||||
* @return the text representation
|
||||
*/
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the syntax style used by this version when writing to a plain-text
|
||||
* data stream.
|
||||
* @return the syntax style
|
||||
*/
|
||||
public SyntaxStyle getSyntaxStyle() {
|
||||
return syntaxStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link ICalVersion} instance based on the given version number.
|
||||
* @param version the version number (e.g. "2.0")
|
||||
* @return the object or null if not found
|
||||
*/
|
||||
public static ICalVersion get(String version) {
|
||||
if (V1_0.version.equals(version)) {
|
||||
return V1_0;
|
||||
}
|
||||
if (V2_0.version.equals(version)) {
|
||||
return V2_0;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this == V2_0_DEPRECATED) {
|
||||
return version + " (obsoleted)";
|
||||
}
|
||||
return version;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,100 +0,0 @@
|
||||
package biweekly;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Singleton for accessing the i18n resource bundle.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public enum Messages {
|
||||
INSTANCE;
|
||||
|
||||
private final transient ResourceBundle messages;
|
||||
|
||||
Messages() {
|
||||
messages = ResourceBundle.getBundle("biweekly.messages");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a validation warning message.
|
||||
* @param code the message code
|
||||
* @param args the message arguments
|
||||
* @return the message
|
||||
*/
|
||||
public String getValidationWarning(int code, Object... args) {
|
||||
return getMessage("validate." + code, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a parser warning message.
|
||||
* @param code the message code
|
||||
* @param args the message arguments
|
||||
* @return the message
|
||||
*/
|
||||
public String getParseMessage(int code, Object... args) {
|
||||
return getMessage("parse." + code, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an exception message.
|
||||
* @param code the message code
|
||||
* @param args the message arguments
|
||||
* @return the message or null if not found
|
||||
*/
|
||||
public String getExceptionMessage(int code, Object... args) {
|
||||
return getMessage("exception." + code, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an {@link IllegalArgumentException} from an exception message.
|
||||
* @param code the message code
|
||||
* @param args the message arguments
|
||||
* @return the exception or null if the message was not found
|
||||
*/
|
||||
public IllegalArgumentException getIllegalArgumentException(int code, Object... args) {
|
||||
String message = getExceptionMessage(code, args);
|
||||
return (message == null) ? null : new IllegalArgumentException(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a message.
|
||||
* @param key the message key
|
||||
* @param args the message arguments
|
||||
* @return the message or null if not found
|
||||
*/
|
||||
public String getMessage(String key, Object... args) {
|
||||
try {
|
||||
String message = messages.getString(key);
|
||||
return MessageFormat.format(message, args);
|
||||
} catch (MissingResourceException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
package biweekly;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a validation warning.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ValidationWarning {
|
||||
private final Integer code;
|
||||
private final String message;
|
||||
|
||||
/**
|
||||
* Creates a new validation warning.
|
||||
* @param code the warning message code
|
||||
* @param args the warning message arguments
|
||||
*/
|
||||
public ValidationWarning(int code, Object... args) {
|
||||
this.code = code;
|
||||
this.message = Messages.INSTANCE.getValidationWarning(code, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new validation warning.
|
||||
* @param message the warning message
|
||||
*/
|
||||
public ValidationWarning(String message) {
|
||||
this.code = null;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the validation warning code.
|
||||
* @return the warning code or null if no code was specified
|
||||
*/
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the validation warning message.
|
||||
* @return the warning message
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (code == null) {
|
||||
return message;
|
||||
}
|
||||
return "(" + code + ") " + message;
|
||||
}
|
||||
}
|
@ -1,289 +0,0 @@
|
||||
package biweekly;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import biweekly.ValidationWarnings.WarningsGroup;
|
||||
import biweekly.component.ICalComponent;
|
||||
import biweekly.property.ICalProperty;
|
||||
import biweekly.util.StringUtils;
|
||||
import biweekly.util.StringUtils.JoinCallback;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Holds the validation warnings of an iCalendar object.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Examples:</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* //validate an iCalendar object
|
||||
* ValidationWarnings warnings = ical.validate();
|
||||
*
|
||||
* //print all warnings to a string:
|
||||
* System.out.println(warnings.toString());
|
||||
* //sample output:
|
||||
* //[ICalendar]: ProductId is not set (it is a required property).
|
||||
* //[ICalendar > VEvent > DateStart]: DateStart must come before DateEnd.
|
||||
* //[ICalendar > VEvent > VAlarm]: The trigger must specify which date field its duration is relative to.
|
||||
*
|
||||
* //iterate over each warnings group
|
||||
* //this gives you access to the property/component object and its parent components
|
||||
* for (WarningsGroup group : warnings) {
|
||||
* ICalProperty prop = group.getProperty();
|
||||
* if (prop == null) {
|
||||
* //then it was a component that caused the warnings
|
||||
* ICalComponent comp = group.getComponent();
|
||||
* }
|
||||
*
|
||||
* //get parent components
|
||||
* List<ICalComponent> hierarchy = group.getComponentHierarchy();
|
||||
*
|
||||
* //get warning messages
|
||||
* List<String> messages = group.getMessages();
|
||||
* }
|
||||
*
|
||||
* //you can also get the warnings of specific properties/components
|
||||
* List<WarningsGroup> dtstartWarnings = warnings.getByProperty(DateStart.class);
|
||||
* List<WarningsGroup> veventWarnings = warnings.getByComponent(VEvent.class);
|
||||
* </pre>
|
||||
* @author Michael Angstadt
|
||||
* @see ICalendar#validate(ICalVersion)
|
||||
*/
|
||||
public class ValidationWarnings implements Iterable<WarningsGroup> {
|
||||
private final List<WarningsGroup> warnings;
|
||||
|
||||
/**
|
||||
* Creates a new validation warnings list.
|
||||
* @param warnings the validation warnings
|
||||
*/
|
||||
public ValidationWarnings(List<WarningsGroup> warnings) {
|
||||
this.warnings = warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all validation warnings of a given property.
|
||||
* @param propertyClass the property (e.g. {@code DateStart.class})
|
||||
* @return the validation warnings
|
||||
*/
|
||||
public List<WarningsGroup> getByProperty(Class<? extends ICalProperty> propertyClass) {
|
||||
List<WarningsGroup> warnings = new ArrayList<WarningsGroup>();
|
||||
for (WarningsGroup group : this.warnings) {
|
||||
ICalProperty property = group.getProperty();
|
||||
if (property == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (propertyClass == property.getClass()) {
|
||||
warnings.add(group);
|
||||
}
|
||||
}
|
||||
return warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all validation warnings of a given component.
|
||||
* @param componentClass the component (e.g. {@code VEvent.class})
|
||||
* @return the validation warnings
|
||||
*/
|
||||
public List<WarningsGroup> getByComponent(Class<? extends ICalComponent> componentClass) {
|
||||
List<WarningsGroup> warnings = new ArrayList<WarningsGroup>();
|
||||
for (WarningsGroup group : this.warnings) {
|
||||
ICalComponent component = group.getComponent();
|
||||
if (component == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (componentClass == component.getClass()) {
|
||||
warnings.add(group);
|
||||
}
|
||||
}
|
||||
return warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the validation warnings.
|
||||
* @return the validation warnings
|
||||
*/
|
||||
public List<WarningsGroup> getWarnings() {
|
||||
return warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether there are any validation warnings.
|
||||
* @return true if there are none, false if there are one or more
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return warnings.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Outputs all validation warnings as a newline-delimited string. For
|
||||
* example:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* [ICalendar]: ProductId is not set (it is a required property).
|
||||
* [ICalendar > VEvent > DateStart]: DateStart must come before DateEnd.
|
||||
* [ICalendar > VEvent > VAlarm]: The trigger must specify which date field its duration is relative to.
|
||||
* </pre>
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringUtils.join(warnings, StringUtils.NEWLINE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over each warning group (same as calling
|
||||
* {@code getWarnings().iterator()}).
|
||||
* @return the iterator
|
||||
*/
|
||||
public Iterator<WarningsGroup> iterator() {
|
||||
return warnings.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds the validation warnings of a property or component.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public static class WarningsGroup {
|
||||
private final ICalProperty property;
|
||||
private final ICalComponent component;
|
||||
private final List<ICalComponent> componentHierarchy;
|
||||
private final List<ValidationWarning> warnings;
|
||||
|
||||
/**
|
||||
* Creates a new set of validation warnings for a property.
|
||||
* @param property the property that caused the warnings
|
||||
* @param componentHierarchy the hierarchy of components that the
|
||||
* property belongs to
|
||||
* @param warning the warnings
|
||||
*/
|
||||
public WarningsGroup(ICalProperty property, List<ICalComponent> componentHierarchy, List<ValidationWarning> warning) {
|
||||
this(null, property, componentHierarchy, warning);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new set of validation warnings for a component.
|
||||
* @param component the component that caused the warnings
|
||||
* @param componentHierarchy the hierarchy of components that the
|
||||
* component belongs to
|
||||
* @param warning the warnings
|
||||
*/
|
||||
public WarningsGroup(ICalComponent component, List<ICalComponent> componentHierarchy, List<ValidationWarning> warning) {
|
||||
this(component, null, componentHierarchy, warning);
|
||||
}
|
||||
|
||||
private WarningsGroup(ICalComponent component, ICalProperty property, List<ICalComponent> componentHierarchy, List<ValidationWarning> warning) {
|
||||
this.component = component;
|
||||
this.property = property;
|
||||
this.componentHierarchy = componentHierarchy;
|
||||
this.warnings = warning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the property object that caused the validation warnings.
|
||||
* @return the property object or null if a component caused the
|
||||
* warnings.
|
||||
*/
|
||||
public ICalProperty getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the component object that caused the validation warnings.
|
||||
* @return the component object or null if a property caused the
|
||||
* warnings.
|
||||
*/
|
||||
public ICalComponent getComponent() {
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hierarchy of components that the property or component
|
||||
* belongs to.
|
||||
* @return the component hierarchy
|
||||
*/
|
||||
public List<ICalComponent> getComponentHierarchy() {
|
||||
return componentHierarchy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the warnings that belong to the property or component.
|
||||
* @return the warnings
|
||||
*/
|
||||
public List<ValidationWarning> getWarnings() {
|
||||
return warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Outputs each message in this warnings group as a newline-delimited
|
||||
* string. Each line includes the component hierarchy and the name of
|
||||
* the property/component. For example:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* [ICalendar > VEvent > VAlarm]: Email alarms must have at least one attendee.
|
||||
* [ICalendar > VEvent > VAlarm]: The trigger must specify which date field its duration is relative to.
|
||||
* </pre>
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
final String prefix = "[" + buildPath() + "]: ";
|
||||
return StringUtils.join(warnings, StringUtils.NEWLINE, new JoinCallback<ValidationWarning>() {
|
||||
public void handle(StringBuilder sb, ValidationWarning warning) {
|
||||
sb.append(prefix).append(warning);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String buildPath() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (!componentHierarchy.isEmpty()) {
|
||||
String delimitor = " > ";
|
||||
|
||||
StringUtils.join(componentHierarchy, delimitor, sb, new JoinCallback<ICalComponent>() {
|
||||
public void handle(StringBuilder sb, ICalComponent component) {
|
||||
sb.append(component.getClass().getSimpleName());
|
||||
}
|
||||
});
|
||||
sb.append(delimitor);
|
||||
}
|
||||
|
||||
Object obj = (property == null) ? component : property;
|
||||
sb.append(obj.getClass().getSimpleName());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
Copyright (c) 2013-2021, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -1,202 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
@ -1,67 +0,0 @@
|
||||
package biweekly.component;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Defines the date range of a timezone's daylight savings time.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Examples:</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* VTimezone timezone = new VTimezone("Eastern Standard Time");
|
||||
* DaylightSavingsTime daylight = new DaylightSavingsTime();
|
||||
* DateTimeComponents components = new DateTimeComponents(1999, 4, 4, 2, 0, 0, false);
|
||||
* daylight.setDateStart(components);
|
||||
* daylight.setTimezoneOffsetFrom(-5, 0);
|
||||
* daylight.setTimezoneOffsetTo(-4, 0);
|
||||
* timezone.addDaylightSavingsTime(daylight);
|
||||
* </pre>
|
||||
* @author Michael Angstadt
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-62">RFC 5545
|
||||
* p.62-71</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-60">RFC 2445 p.60-7</a>
|
||||
*/
|
||||
public class DaylightSavingsTime extends Observance {
|
||||
public DaylightSavingsTime() {
|
||||
//empty
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
* @param original the component to make a copy of
|
||||
*/
|
||||
public DaylightSavingsTime(DaylightSavingsTime original) {
|
||||
super(original);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DaylightSavingsTime copy() {
|
||||
return new DaylightSavingsTime(this);
|
||||
}
|
||||
}
|
@ -1,833 +0,0 @@
|
||||
package biweekly.component;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import biweekly.ICalDataType;
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.ICalendar;
|
||||
import biweekly.Messages;
|
||||
import biweekly.ValidationWarnings.WarningsGroup;
|
||||
import biweekly.ValidationWarning;
|
||||
import biweekly.property.ICalProperty;
|
||||
import biweekly.property.RawProperty;
|
||||
import biweekly.property.Status;
|
||||
import biweekly.util.ListMultimap;
|
||||
import biweekly.util.StringUtils;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base class for all iCalendar component classes.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public abstract class ICalComponent {
|
||||
protected final ListMultimap<Class<? extends ICalComponent>, ICalComponent> components;
|
||||
protected final ListMultimap<Class<? extends ICalProperty>, ICalProperty> properties;
|
||||
|
||||
public ICalComponent() {
|
||||
components = new ListMultimap<Class<? extends ICalComponent>, ICalComponent>();
|
||||
properties = new ListMultimap<Class<? extends ICalProperty>, ICalProperty>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor. Performs a deep copy of the given component's
|
||||
* properties and sub-components.
|
||||
* @param original the component to make a copy of
|
||||
*/
|
||||
protected ICalComponent(ICalComponent original) {
|
||||
this();
|
||||
for (ICalProperty property : original.properties.values()) {
|
||||
addProperty(property.copy());
|
||||
}
|
||||
for (ICalComponent component : original.components.values()) {
|
||||
addComponent(component.copy());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first property of a given class.
|
||||
* @param clazz the property class
|
||||
* @param <T> the property class
|
||||
* @return the property or null if not found
|
||||
*/
|
||||
public <T extends ICalProperty> T getProperty(Class<T> clazz) {
|
||||
return clazz.cast(properties.first(clazz));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all properties of a given class. Changes to the returned list will
|
||||
* update the {@link ICalComponent} object, and vice versa.
|
||||
* @param clazz the property class
|
||||
* @param <T> the property class
|
||||
* @return the properties
|
||||
*/
|
||||
public <T extends ICalProperty> List<T> getProperties(Class<T> clazz) {
|
||||
return new ICalPropertyList<T>(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the properties associated with this component.
|
||||
* @return the properties
|
||||
*/
|
||||
public ListMultimap<Class<? extends ICalProperty>, ICalProperty> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a property to this component.
|
||||
* @param property the property to add
|
||||
*/
|
||||
public void addProperty(ICalProperty property) {
|
||||
properties.put(property.getClass(), property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all existing properties of the given property instance's class
|
||||
* with the given property instance.
|
||||
* @param property the property
|
||||
* @return the replaced properties (this list is immutable)
|
||||
*/
|
||||
public List<ICalProperty> setProperty(ICalProperty property) {
|
||||
return properties.replace(property.getClass(), property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all existing properties of the given class with a single
|
||||
* property instance. If the property instance is null, then all instances
|
||||
* of that property will be removed.
|
||||
* @param clazz the property class (e.g. "DateStart.class")
|
||||
* @param property the property or null to remove all properties of the
|
||||
* given class
|
||||
* @param <T> the property class
|
||||
* @return the replaced properties (this list is immutable)
|
||||
*/
|
||||
public <T extends ICalProperty> List<T> setProperty(Class<T> clazz, T property) {
|
||||
List<ICalProperty> replaced = properties.replace(clazz, property);
|
||||
return castList(replaced, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a specific property instance from this component.
|
||||
* @param property the property to remove
|
||||
* @param <T> the property class
|
||||
* @return true if it was removed, false if it wasn't found
|
||||
*/
|
||||
public <T extends ICalProperty> boolean removeProperty(T property) {
|
||||
return properties.remove(property.getClass(), property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all properties of a given class from this component.
|
||||
* @param clazz the class of the properties to remove (e.g.
|
||||
* "DateStart.class")
|
||||
* @param <T> the property class
|
||||
* @return the removed properties (this list is immutable)
|
||||
*/
|
||||
public <T extends ICalProperty> List<T> removeProperties(Class<T> clazz) {
|
||||
List<ICalProperty> removed = properties.removeAll(clazz);
|
||||
return castList(removed, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a specific sub-component instance from this component.
|
||||
* @param component the component to remove
|
||||
* @param <T> the component class
|
||||
* @return true if it was removed, false if it wasn't found
|
||||
*/
|
||||
public <T extends ICalComponent> boolean removeComponent(T component) {
|
||||
return components.remove(component.getClass(), component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all sub-components of the given class from this component.
|
||||
* @param clazz the class of the components to remove (e.g. "VEvent.class")
|
||||
* @param <T> the component class
|
||||
* @return the removed components (this list is immutable)
|
||||
*/
|
||||
public <T extends ICalComponent> List<T> removeComponents(Class<T> clazz) {
|
||||
List<ICalComponent> removed = components.removeAll(clazz);
|
||||
return castList(removed, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first experimental property with a given name.
|
||||
* @param name the property name (case insensitive, e.g. "X-ALT-DESC")
|
||||
* @return the experimental property or null if none were found
|
||||
*/
|
||||
public RawProperty getExperimentalProperty(String name) {
|
||||
for (RawProperty raw : getExperimentalProperties()) {
|
||||
if (raw.getName().equalsIgnoreCase(name)) {
|
||||
return raw;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all experimental properties with a given name.
|
||||
* @param name the property name (case insensitive, e.g. "X-ALT-DESC")
|
||||
* @return the experimental properties (this list is immutable)
|
||||
*/
|
||||
public List<RawProperty> getExperimentalProperties(String name) {
|
||||
/*
|
||||
* Note: The returned list is not backed by the parent component because
|
||||
* this would allow RawProperty objects without the specified name to be
|
||||
* added to the list.
|
||||
*/
|
||||
List<RawProperty> toReturn = new ArrayList<RawProperty>();
|
||||
for (RawProperty property : getExperimentalProperties()) {
|
||||
if (property.getName().equalsIgnoreCase(name)) {
|
||||
toReturn.add(property);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(toReturn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all experimental properties associated with this component. Changes
|
||||
* to the returned list will update the {@link ICalComponent} object, and
|
||||
* vice versa.
|
||||
* @return the experimental properties
|
||||
*/
|
||||
public List<RawProperty> getExperimentalProperties() {
|
||||
return getProperties(RawProperty.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an experimental property to this component.
|
||||
* @param name the property name (e.g. "X-ALT-DESC")
|
||||
* @param value the property value
|
||||
* @return the property object that was created
|
||||
*/
|
||||
public RawProperty addExperimentalProperty(String name, String value) {
|
||||
return addExperimentalProperty(name, null, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an experimental property to this component.
|
||||
* @param name the property name (e.g. "X-ALT-DESC")
|
||||
* @param dataType the property's data type or null if unknown
|
||||
* @param value the property value
|
||||
* @return the property object that was created
|
||||
*/
|
||||
public RawProperty addExperimentalProperty(String name, ICalDataType dataType, String value) {
|
||||
RawProperty raw = new RawProperty(name, dataType, value);
|
||||
addProperty(raw);
|
||||
return raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an experimental property to this component, removing all existing
|
||||
* properties that have the same name.
|
||||
* @param name the property name (e.g. "X-ALT-DESC")
|
||||
* @param value the property value
|
||||
* @return the property object that was created
|
||||
*/
|
||||
public RawProperty setExperimentalProperty(String name, String value) {
|
||||
return setExperimentalProperty(name, null, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an experimental property to this component, removing all existing
|
||||
* properties that have the same name.
|
||||
* @param name the property name (e.g. "X-ALT-DESC")
|
||||
* @param dataType the property's data type or null if unknown
|
||||
* @param value the property value
|
||||
* @return the property object that was created
|
||||
*/
|
||||
public RawProperty setExperimentalProperty(String name, ICalDataType dataType, String value) {
|
||||
removeExperimentalProperties(name);
|
||||
return addExperimentalProperty(name, dataType, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all experimental properties that have the given name.
|
||||
* @param name the component name (e.g. "X-ALT-DESC")
|
||||
* @return the removed properties (this list is immutable)
|
||||
*/
|
||||
public List<RawProperty> removeExperimentalProperties(String name) {
|
||||
List<RawProperty> all = getExperimentalProperties();
|
||||
List<RawProperty> toRemove = new ArrayList<RawProperty>();
|
||||
for (RawProperty property : all) {
|
||||
if (property.getName().equalsIgnoreCase(name)) {
|
||||
toRemove.add(property);
|
||||
}
|
||||
}
|
||||
|
||||
all.removeAll(toRemove);
|
||||
return Collections.unmodifiableList(toRemove);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first sub-component of a given class.
|
||||
* @param clazz the component class
|
||||
* @param <T> the component class
|
||||
* @return the sub-component or null if not found
|
||||
*/
|
||||
public <T extends ICalComponent> T getComponent(Class<T> clazz) {
|
||||
return clazz.cast(components.first(clazz));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all sub-components of a given class. Changes to the returned list
|
||||
* will update the parent component object, and vice versa.
|
||||
* @param clazz the component class
|
||||
* @param <T> the component class
|
||||
* @return the sub-components
|
||||
*/
|
||||
public <T extends ICalComponent> List<T> getComponents(Class<T> clazz) {
|
||||
return new ICalComponentList<T>(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the sub-components associated with this component.
|
||||
* @return the sub-components
|
||||
*/
|
||||
public ListMultimap<Class<? extends ICalComponent>, ICalComponent> getComponents() {
|
||||
return components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a sub-component to this component.
|
||||
* @param component the component to add
|
||||
*/
|
||||
public void addComponent(ICalComponent component) {
|
||||
components.put(component.getClass(), component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all sub-components of a given class with the given component.
|
||||
* @param component the component
|
||||
* @return the replaced sub-components (this list is immutable)
|
||||
*/
|
||||
public List<ICalComponent> setComponent(ICalComponent component) {
|
||||
return components.replace(component.getClass(), component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all sub-components of a given class with the given component. If
|
||||
* the component instance is null, then all instances of that component will
|
||||
* be removed.
|
||||
* @param clazz the component's class
|
||||
* @param component the component or null to remove all components of the
|
||||
* given class
|
||||
* @param <T> the component class
|
||||
* @return the replaced sub-components (this list is immutable)
|
||||
*/
|
||||
public <T extends ICalComponent> List<T> setComponent(Class<T> clazz, T component) {
|
||||
List<ICalComponent> replaced = components.replace(clazz, component);
|
||||
return castList(replaced, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first experimental sub-component with a given name.
|
||||
* @param name the component name (case insensitive, e.g. "X-PARTY")
|
||||
* @return the experimental component or null if none were found
|
||||
*/
|
||||
public RawComponent getExperimentalComponent(String name) {
|
||||
for (RawComponent component : getExperimentalComponents()) {
|
||||
if (component.getName().equalsIgnoreCase(name)) {
|
||||
return component;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all experimental sub-component with a given name.
|
||||
* @param name the component name (case insensitive, e.g. "X-PARTY")
|
||||
* @return the experimental components (this list is immutable)
|
||||
*/
|
||||
public List<RawComponent> getExperimentalComponents(String name) {
|
||||
/*
|
||||
* Note: The returned list is not backed by the parent component because
|
||||
* this would allow RawComponent objects without the specified name to
|
||||
* be added to the list.
|
||||
*/
|
||||
List<RawComponent> toReturn = new ArrayList<RawComponent>();
|
||||
for (RawComponent component : getExperimentalComponents()) {
|
||||
if (component.getName().equalsIgnoreCase(name)) {
|
||||
toReturn.add(component);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(toReturn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all experimental sub-components associated with this component.
|
||||
* Changes to the returned list will update the parent {@link ICalComponent}
|
||||
* object, and vice versa.
|
||||
* @return the experimental components
|
||||
*/
|
||||
public List<RawComponent> getExperimentalComponents() {
|
||||
return getComponents(RawComponent.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an experimental sub-component to this component.
|
||||
* @param name the component name (e.g. "X-PARTY")
|
||||
* @return the component object that was created
|
||||
*/
|
||||
public RawComponent addExperimentalComponent(String name) {
|
||||
RawComponent raw = new RawComponent(name);
|
||||
addComponent(raw);
|
||||
return raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an experimental sub-component to this component, removing all
|
||||
* existing components that have the same name.
|
||||
* @param name the component name (e.g. "X-PARTY")
|
||||
* @return the component object that was created
|
||||
*/
|
||||
public RawComponent setExperimentalComponent(String name) {
|
||||
removeExperimentalComponents(name);
|
||||
return addExperimentalComponent(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all experimental sub-components that have the given name.
|
||||
* @param name the component name (e.g. "X-PARTY")
|
||||
* @return the removed sub-components (this list is immutable)
|
||||
*/
|
||||
public List<RawComponent> removeExperimentalComponents(String name) {
|
||||
List<RawComponent> all = getExperimentalComponents();
|
||||
List<RawComponent> toRemove = new ArrayList<RawComponent>();
|
||||
for (RawComponent property : all) {
|
||||
if (property.getName().equalsIgnoreCase(name)) {
|
||||
toRemove.add(property);
|
||||
}
|
||||
}
|
||||
|
||||
all.removeAll(toRemove);
|
||||
return Collections.unmodifiableList(toRemove);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Checks this component for data consistency problems or deviations from
|
||||
* the specifications.
|
||||
* </p>
|
||||
* <p>
|
||||
* The existence of validation warnings will not prevent the component
|
||||
* object from being written to a data stream. Syntactically-correct output
|
||||
* will still be produced. However, the consuming application may have
|
||||
* trouble interpreting some of the data due to the presence of these
|
||||
* warnings.
|
||||
* </p>
|
||||
* <p>
|
||||
* These problems can largely be avoided by reading the Javadocs of the
|
||||
* component and property classes, or by being familiar with the iCalendar
|
||||
* standard.
|
||||
* </p>
|
||||
* @param hierarchy the hierarchy of components that the component belongs
|
||||
* to
|
||||
* @param version the version to validate against
|
||||
* @see ICalendar#validate(List, ICalVersion)
|
||||
* @return a list of warnings or an empty list if no problems were found
|
||||
*/
|
||||
public final List<WarningsGroup> validate(List<ICalComponent> hierarchy, ICalVersion version) {
|
||||
List<WarningsGroup> warnings = new ArrayList<WarningsGroup>();
|
||||
|
||||
//validate this component
|
||||
List<ValidationWarning> warningsBuf = new ArrayList<ValidationWarning>(0);
|
||||
validate(hierarchy, version, warningsBuf);
|
||||
if (!warningsBuf.isEmpty()) {
|
||||
warnings.add(new WarningsGroup(this, hierarchy, warningsBuf));
|
||||
}
|
||||
|
||||
//add this component to the hierarchy list
|
||||
//copy the list so other validate() calls aren't effected
|
||||
hierarchy = new ArrayList<ICalComponent>(hierarchy);
|
||||
hierarchy.add(this);
|
||||
|
||||
//validate properties
|
||||
for (ICalProperty property : properties.values()) {
|
||||
List<ValidationWarning> propWarnings = property.validate(hierarchy, version);
|
||||
if (!propWarnings.isEmpty()) {
|
||||
warnings.add(new WarningsGroup(property, hierarchy, propWarnings));
|
||||
}
|
||||
}
|
||||
|
||||
//validate sub-components
|
||||
for (ICalComponent component : components.values()) {
|
||||
warnings.addAll(component.validate(hierarchy, version));
|
||||
}
|
||||
|
||||
return warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Checks the component for data consistency problems or deviations from the
|
||||
* spec.
|
||||
* </p>
|
||||
* <p>
|
||||
* This method should be overridden by child classes that wish to provide
|
||||
* validation logic. The default implementation of this method does nothing.
|
||||
* </p>
|
||||
* @param components the hierarchy of components that the component belongs
|
||||
* to
|
||||
* @param version the version to validate against
|
||||
* @param warnings the list to add the warnings to
|
||||
*/
|
||||
protected void validate(List<ICalComponent> components, ICalVersion version, List<ValidationWarning> warnings) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for validating that there is exactly one instance of each
|
||||
* of the given properties.
|
||||
* @param warnings the list to add the warnings to
|
||||
* @param classes the properties to check
|
||||
*/
|
||||
protected void checkRequiredCardinality(List<ValidationWarning> warnings, Class<? extends ICalProperty>... classes) {
|
||||
for (Class<? extends ICalProperty> clazz : classes) {
|
||||
List<? extends ICalProperty> props = getProperties(clazz);
|
||||
|
||||
if (props.isEmpty()) {
|
||||
warnings.add(new ValidationWarning(2, clazz.getSimpleName()));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (props.size() > 1) {
|
||||
warnings.add(new ValidationWarning(3, clazz.getSimpleName()));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for validating that there is no more than one instance of
|
||||
* each of the given properties.
|
||||
* @param warnings the list to add the warnings to
|
||||
* @param classes the properties to check
|
||||
*/
|
||||
protected void checkOptionalCardinality(List<ValidationWarning> warnings, Class<? extends ICalProperty>... classes) {
|
||||
for (Class<? extends ICalProperty> clazz : classes) {
|
||||
List<? extends ICalProperty> props = getProperties(clazz);
|
||||
|
||||
if (props.size() > 1) {
|
||||
warnings.add(new ValidationWarning(3, clazz.getSimpleName()));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for validating the {@link Status} property of a component.
|
||||
* @param warnings the list to add the warnings to
|
||||
* @param allowed the valid statuses
|
||||
*/
|
||||
protected void checkStatus(List<ValidationWarning> warnings, Status... allowed) {
|
||||
Status actual = getProperty(Status.class);
|
||||
if (actual == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> allowedValues = new ArrayList<String>(allowed.length);
|
||||
for (Status status : allowed) {
|
||||
String value = status.getValue().toLowerCase();
|
||||
allowedValues.add(value);
|
||||
}
|
||||
|
||||
String actualValue = actual.getValue().toLowerCase();
|
||||
if (!allowedValues.contains(actualValue)) {
|
||||
warnings.add(new ValidationWarning(13, actual.getValue(), allowedValues));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
toString(0, sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gets string representations of any additional fields the component has
|
||||
* (other than sub-components and properties) for the {@link #toString}
|
||||
* method.
|
||||
* </p>
|
||||
* <p>
|
||||
* Meant to be overridden by child classes. The default implementation
|
||||
* returns an empty map.
|
||||
* </p>
|
||||
* @return the values of the component's fields (key = field name, value =
|
||||
* field value)
|
||||
*/
|
||||
protected Map<String, Object> toStringValues() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
private void toString(int depth, StringBuilder sb) {
|
||||
StringUtils.repeat(' ', depth * 2, sb);
|
||||
sb.append(getClass().getName());
|
||||
|
||||
Map<String, Object> fields = toStringValues();
|
||||
if (!fields.isEmpty()) {
|
||||
sb.append(' ').append(fields.toString());
|
||||
}
|
||||
|
||||
sb.append(StringUtils.NEWLINE);
|
||||
|
||||
depth++;
|
||||
for (ICalProperty property : properties.values()) {
|
||||
StringUtils.repeat(' ', depth * 2, sb);
|
||||
sb.append(property).append(StringUtils.NEWLINE);
|
||||
}
|
||||
for (ICalComponent component : components.values()) {
|
||||
component.toString(depth, sb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Creates a deep copy of this component object.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default implementation of this method uses reflection to look for a
|
||||
* copy constructor. Child classes SHOULD override this method to avoid the
|
||||
* performance overhead involved in using reflection.
|
||||
* </p>
|
||||
* <p>
|
||||
* The child class's copy constructor, if present, MUST invoke the
|
||||
* {@link #ICalComponent(ICalComponent)} super constructor to ensure that
|
||||
* the component's properties and sub-components are copied.
|
||||
* </p>
|
||||
* <p>
|
||||
* This method MUST be overridden by the child class if the child class does
|
||||
* not have a copy constructor. Otherwise, an
|
||||
* {@link UnsupportedOperationException} will be thrown when an attempt is
|
||||
* made to copy the component (such as in the
|
||||
* {@link ICalendar#ICalendar(ICalendar) ICalendar class's copy constructor}
|
||||
* ).
|
||||
* </p>
|
||||
* @return the copy
|
||||
* @throws UnsupportedOperationException if the class does not have a copy
|
||||
* constructor or there is a problem invoking it
|
||||
*/
|
||||
public ICalComponent copy() {
|
||||
Class<? extends ICalComponent> clazz = getClass();
|
||||
|
||||
try {
|
||||
Constructor<? extends ICalComponent> copyConstructor = clazz.getConstructor(clazz);
|
||||
return copyConstructor.newInstance(this);
|
||||
} catch (Exception e) {
|
||||
throw new UnsupportedOperationException(Messages.INSTANCE.getExceptionMessage(1, clazz.getName()), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts all objects in the given list to the given class, adding the casted
|
||||
* objects to a new list.
|
||||
* @param list the list to cast
|
||||
* @param castTo the class to cast to
|
||||
* @param <T> the class to cast to
|
||||
* @return the new list (immutable)
|
||||
*/
|
||||
private static <T> List<T> castList(List<?> list, Class<T> castTo) {
|
||||
List<T> casted = new ArrayList<T>(list.size());
|
||||
for (Object object : list) {
|
||||
casted.add(castTo.cast(object));
|
||||
}
|
||||
return Collections.unmodifiableList(casted);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
|
||||
int propertiesHash = 1;
|
||||
for (ICalProperty property : properties.values()) {
|
||||
propertiesHash += property.hashCode();
|
||||
}
|
||||
result = prime * result + propertiesHash;
|
||||
|
||||
int componentsHash = 1;
|
||||
for (ICalComponent component : components.values()) {
|
||||
componentsHash += component.hashCode();
|
||||
}
|
||||
result = prime * result + componentsHash;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
ICalComponent other = (ICalComponent) obj;
|
||||
|
||||
if (properties.size() != other.properties.size()) return false;
|
||||
if (components.size() != other.components.size()) return false;
|
||||
|
||||
if (!compareMultimaps(properties, other.properties)) return false;
|
||||
if (!compareMultimaps(components, other.components)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static <K, V> boolean compareMultimaps(ListMultimap<K, V> map1, ListMultimap<K, V> map2) {
|
||||
for (Map.Entry<K, List<V>> entry : map1) {
|
||||
K key = entry.getKey();
|
||||
List<V> value = entry.getValue();
|
||||
List<V> otherValue = map2.get(key);
|
||||
|
||||
if (value.size() != otherValue.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<V> otherValueCopy = new ArrayList<V>(otherValue);
|
||||
for (V property : value) {
|
||||
if (!otherValueCopy.remove(property)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A list that automatically casts {@link ICalComponent} instances stored in
|
||||
* this component to a given component class.
|
||||
* </p>
|
||||
* <p>
|
||||
* This list is backed by the {@link ICalComponent} object. Any changes made
|
||||
* to the list will affect the {@link ICalComponent} object and vice versa.
|
||||
* </p>
|
||||
* @param <T> the component class
|
||||
*/
|
||||
private class ICalComponentList<T extends ICalComponent> extends AbstractList<T> {
|
||||
protected final Class<T> componentClass;
|
||||
protected final List<ICalComponent> components;
|
||||
|
||||
/**
|
||||
* @param componentClass the component class
|
||||
*/
|
||||
public ICalComponentList(Class<T> componentClass) {
|
||||
this.componentClass = componentClass;
|
||||
components = ICalComponent.this.components.get(componentClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, T value) {
|
||||
components.add(index, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T remove(int index) {
|
||||
ICalComponent removed = components.remove(index);
|
||||
return cast(removed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(int index) {
|
||||
ICalComponent property = components.get(index);
|
||||
return cast(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T set(int index, T value) {
|
||||
ICalComponent replaced = components.set(index, value);
|
||||
return cast(replaced);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return components.size();
|
||||
}
|
||||
|
||||
protected T cast(ICalComponent value) {
|
||||
return componentClass.cast(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A list that automatically casts {@link ICalProperty} instances stored in
|
||||
* this component to a given property class.
|
||||
* </p>
|
||||
* <p>
|
||||
* This list is backed by the {@link ICalComponent} object. Any changes made
|
||||
* to the list will affect the {@link ICalComponent} object and vice versa.
|
||||
* </p>
|
||||
* @param <T> the property class
|
||||
*/
|
||||
private class ICalPropertyList<T extends ICalProperty> extends AbstractList<T> {
|
||||
protected final Class<T> propertyClass;
|
||||
protected final List<ICalProperty> properties;
|
||||
|
||||
/**
|
||||
* @param propertyClass the property class
|
||||
*/
|
||||
public ICalPropertyList(Class<T> propertyClass) {
|
||||
this.propertyClass = propertyClass;
|
||||
properties = ICalComponent.this.properties.get(propertyClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, T value) {
|
||||
properties.add(index, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T remove(int index) {
|
||||
ICalProperty removed = properties.remove(index);
|
||||
return cast(removed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(int index) {
|
||||
ICalProperty property = properties.get(index);
|
||||
return cast(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T set(int index, T value) {
|
||||
ICalProperty replaced = properties.set(index, value);
|
||||
return cast(replaced);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return properties.size();
|
||||
}
|
||||
|
||||
protected T cast(ICalProperty value) {
|
||||
return propertyClass.cast(value);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,409 +0,0 @@
|
||||
package biweekly.component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.ValidationWarning;
|
||||
import biweekly.property.Comment;
|
||||
import biweekly.property.DateStart;
|
||||
import biweekly.property.ExceptionDates;
|
||||
import biweekly.property.RecurrenceDates;
|
||||
import biweekly.property.RecurrenceRule;
|
||||
import biweekly.property.TimezoneName;
|
||||
import biweekly.property.TimezoneOffsetFrom;
|
||||
import biweekly.property.TimezoneOffsetTo;
|
||||
import biweekly.util.DateTimeComponents;
|
||||
import biweekly.util.ICalDate;
|
||||
import biweekly.util.Recurrence;
|
||||
import biweekly.util.UtcOffset;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a timezone observance (i.e. "daylight savings" and "standard"
|
||||
* times).
|
||||
* @author Michael Angstadt
|
||||
* @see DaylightSavingsTime
|
||||
* @see StandardTime
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-62">RFC 5545
|
||||
* p.62-71</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-60">RFC 2445 p.60-7</a>
|
||||
*/
|
||||
/*
|
||||
* Note: References to the vCal 1.0 spec are omitted from the property
|
||||
* getter/setter method Javadocs because vCal does not use the VTIMEZONE
|
||||
* component.
|
||||
*/
|
||||
public class Observance extends ICalComponent {
|
||||
public Observance() {
|
||||
//empty
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
* @param original the component to make a copy of
|
||||
*/
|
||||
public Observance(Observance original) {
|
||||
super(original);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date that the timezone observance starts.
|
||||
* @return the start date or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-97">RFC 5545
|
||||
* p.97-8</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-93">RFC 2445
|
||||
* p.93-4</a>
|
||||
*/
|
||||
public DateStart getDateStart() {
|
||||
return getProperty(DateStart.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date that the timezone observance starts.
|
||||
* @param dateStart the start date or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-97">RFC 5545
|
||||
* p.97-8</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-93">RFC 2445
|
||||
* p.93-4</a>
|
||||
*/
|
||||
public void setDateStart(DateStart dateStart) {
|
||||
setProperty(DateStart.class, dateStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date that the timezone observance starts.
|
||||
* @param date the start date or null to remove
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-97">RFC 5545
|
||||
* p.97-8</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-93">RFC 2445
|
||||
* p.93-4</a>
|
||||
*/
|
||||
public DateStart setDateStart(ICalDate date) {
|
||||
DateStart prop = (date == null) ? null : new DateStart(date);
|
||||
setDateStart(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date that the timezone observance starts.
|
||||
* @param rawComponents the start date or null to remove
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-97">RFC 5545
|
||||
* p.97-8</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-93">RFC 2445
|
||||
* p.93-4</a>
|
||||
*/
|
||||
public DateStart setDateStart(DateTimeComponents rawComponents) {
|
||||
return setDateStart((rawComponents == null) ? null : new ICalDate(rawComponents, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UTC offset that the timezone observance transitions to.
|
||||
* @return the UTC offset or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-105">RFC 5545
|
||||
* p.105-6</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-100">RFC 2445
|
||||
* p.100-1</a>
|
||||
*/
|
||||
public TimezoneOffsetTo getTimezoneOffsetTo() {
|
||||
return getProperty(TimezoneOffsetTo.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the UTC offset that the timezone observance transitions to.
|
||||
* @param timezoneOffsetTo the UTC offset or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-105">RFC 5545
|
||||
* p.105-6</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-100">RFC 2445
|
||||
* p.100-1</a>
|
||||
*/
|
||||
public void setTimezoneOffsetTo(TimezoneOffsetTo timezoneOffsetTo) {
|
||||
setProperty(TimezoneOffsetTo.class, timezoneOffsetTo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the UTC offset that the timezone observance transitions to.
|
||||
* @param offset the offset
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-105">RFC 5545
|
||||
* p.105-6</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-100">RFC 2445
|
||||
* p.100-1</a>
|
||||
*/
|
||||
public TimezoneOffsetTo setTimezoneOffsetTo(UtcOffset offset) {
|
||||
TimezoneOffsetTo prop = new TimezoneOffsetTo(offset);
|
||||
setTimezoneOffsetTo(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UTC offset that the timezone observance transitions from.
|
||||
* @return the UTC offset or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-104">RFC 5545
|
||||
* p.104-5</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-99">RFC 2445
|
||||
* p.99-100</a>
|
||||
*/
|
||||
public TimezoneOffsetFrom getTimezoneOffsetFrom() {
|
||||
return getProperty(TimezoneOffsetFrom.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the UTC offset that the timezone observance transitions from.
|
||||
* @param timezoneOffsetFrom the UTC offset or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-104">RFC 5545
|
||||
* p.104-5</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-99">RFC 2445
|
||||
* p.99-100</a>
|
||||
*/
|
||||
public void setTimezoneOffsetFrom(TimezoneOffsetFrom timezoneOffsetFrom) {
|
||||
setProperty(TimezoneOffsetFrom.class, timezoneOffsetFrom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the UTC offset that the timezone observance transitions from.
|
||||
* @param offset the offset
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-104">RFC 5545
|
||||
* p.104-5</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-99">RFC 2445
|
||||
* p.99-100</a>
|
||||
*/
|
||||
public TimezoneOffsetFrom setTimezoneOffsetFrom(UtcOffset offset) {
|
||||
TimezoneOffsetFrom prop = new TimezoneOffsetFrom(offset);
|
||||
setTimezoneOffsetFrom(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets how often the timezone observance repeats.
|
||||
* @return the recurrence rule or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-122">RFC 5545
|
||||
* p.122-32</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-117">RFC 2445
|
||||
* p.117-25</a>
|
||||
*/
|
||||
public RecurrenceRule getRecurrenceRule() {
|
||||
return getProperty(RecurrenceRule.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets how often the timezone observance repeats.
|
||||
* @param recur the recurrence rule or null to remove
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-122">RFC 5545
|
||||
* p.122-32</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-117">RFC 2445
|
||||
* p.117-25</a>
|
||||
*/
|
||||
public RecurrenceRule setRecurrenceRule(Recurrence recur) {
|
||||
RecurrenceRule prop = (recur == null) ? null : new RecurrenceRule(recur);
|
||||
setRecurrenceRule(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets how often the timezone observance repeats.
|
||||
* @param recurrenceRule the recurrence rule or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-122">RFC 5545
|
||||
* p.122-32</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-117">RFC 2445
|
||||
* p.117-25</a>
|
||||
*/
|
||||
public void setRecurrenceRule(RecurrenceRule recurrenceRule) {
|
||||
setProperty(RecurrenceRule.class, recurrenceRule);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the comments attached to the timezone observance.
|
||||
* @return the comments (any changes made this list will affect the parent
|
||||
* component object and vice versa)
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-83">RFC 5545
|
||||
* p.83-4</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-80">RFC 2445
|
||||
* p.80-1</a>
|
||||
*/
|
||||
public List<Comment> getComments() {
|
||||
return getProperties(Comment.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a comment to the timezone observance.
|
||||
* @param comment the comment to add
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-83">RFC 5545
|
||||
* p.83-4</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-80">RFC 2445
|
||||
* p.80-1</a>
|
||||
*/
|
||||
public void addComment(Comment comment) {
|
||||
addProperty(comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a comment to the timezone observance.
|
||||
* @param comment the comment to add
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-83">RFC 5545
|
||||
* p.83-4</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-80">RFC 2445
|
||||
* p.80-1</a>
|
||||
*/
|
||||
public Comment addComment(String comment) {
|
||||
Comment prop = new Comment(comment);
|
||||
addComment(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of dates/periods that help define the recurrence rule of
|
||||
* this timezone observance (if one is defined).
|
||||
* @return the recurrence dates (any changes made this list will affect the
|
||||
* parent component object and vice versa)
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-120">RFC 5545
|
||||
* p.120-2</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-115">RFC 2445
|
||||
* p.115-7</a>
|
||||
*/
|
||||
public List<RecurrenceDates> getRecurrenceDates() {
|
||||
return getProperties(RecurrenceDates.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a list of dates/periods that help define the recurrence rule of this
|
||||
* timezone observance (if one is defined).
|
||||
* @param recurrenceDates the recurrence dates
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-120">RFC 5545
|
||||
* p.120-2</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-115">RFC 2445
|
||||
* p.115-7</a>
|
||||
*/
|
||||
public void addRecurrenceDates(RecurrenceDates recurrenceDates) {
|
||||
addProperty(recurrenceDates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the traditional, non-standard names for the timezone observance.
|
||||
* @return the timezone observance names (any changes made this list will
|
||||
* affect the parent component object and vice versa)
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-103">RFC 5545
|
||||
* p.103-4</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-98">RFC 2445
|
||||
* p.98-9</a>
|
||||
*/
|
||||
public List<TimezoneName> getTimezoneNames() {
|
||||
return getProperties(TimezoneName.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a traditional, non-standard name for the timezone observance.
|
||||
* @param timezoneName the timezone observance name
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-103">RFC 5545
|
||||
* p.103-4</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-98">RFC 2445
|
||||
* p.98-9</a>
|
||||
*/
|
||||
public void addTimezoneName(TimezoneName timezoneName) {
|
||||
addProperty(timezoneName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a traditional, non-standard name for the timezone observance.
|
||||
* @param timezoneName the timezone observance name (e.g. "EST")
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-103">RFC 5545
|
||||
* p.103-4</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-98">RFC 2445
|
||||
* p.98-9</a>
|
||||
*/
|
||||
public TimezoneName addTimezoneName(String timezoneName) {
|
||||
TimezoneName prop = new TimezoneName(timezoneName);
|
||||
addTimezoneName(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of exceptions to the timezone observance.
|
||||
* @return the list of exceptions (any changes made this list will affect
|
||||
* the parent component object and vice versa)
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-118">RFC 5545
|
||||
* p.118-20</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-112">RFC 2445
|
||||
* p.112-4</a>
|
||||
*/
|
||||
public List<ExceptionDates> getExceptionDates() {
|
||||
return getProperties(ExceptionDates.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a list of exceptions to the timezone observance. Note that this
|
||||
* property can contain multiple dates.
|
||||
* @param exceptionDates the list of exceptions
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-118">RFC 5545
|
||||
* p.118-20</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-112">RFC 2445
|
||||
* p.112-4</a>
|
||||
*/
|
||||
public void addExceptionDates(ExceptionDates exceptionDates) {
|
||||
addProperty(exceptionDates);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void validate(List<ICalComponent> components, ICalVersion version, List<ValidationWarning> warnings) {
|
||||
if (version == ICalVersion.V1_0) {
|
||||
warnings.add(new ValidationWarning(48, version));
|
||||
}
|
||||
|
||||
checkRequiredCardinality(warnings, DateStart.class, TimezoneOffsetTo.class, TimezoneOffsetFrom.class);
|
||||
|
||||
//BYHOUR, BYMINUTE, and BYSECOND cannot be specified in RRULE if DTSTART's data type is "date"
|
||||
//RFC 5545 p. 167
|
||||
DateStart dateStart = getDateStart();
|
||||
RecurrenceRule rrule = getRecurrenceRule();
|
||||
if (dateStart != null && rrule != null) {
|
||||
ICalDate start = dateStart.getValue();
|
||||
Recurrence recur = rrule.getValue();
|
||||
if (start != null && recur != null) {
|
||||
if (!start.hasTime() && (!recur.getByHour().isEmpty() || !recur.getByMinute().isEmpty() || !recur.getBySecond().isEmpty())) {
|
||||
warnings.add(new ValidationWarning(5));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//there *should* be only 1 instance of RRULE
|
||||
//RFC 5545 p. 167
|
||||
if (getProperties(RecurrenceRule.class).size() > 1) {
|
||||
warnings.add(new ValidationWarning(6));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observance copy() {
|
||||
return new Observance(this);
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
package biweekly.component;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a component that does not have a scribe associated with it.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class RawComponent extends ICalComponent {
|
||||
private final String name;
|
||||
|
||||
public RawComponent(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
* @param original the component to make a copy of
|
||||
*/
|
||||
public RawComponent(RawComponent original) {
|
||||
super(original);
|
||||
this.name = original.name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RawComponent copy() {
|
||||
return new RawComponent(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!super.equals(obj)) return false;
|
||||
RawComponent other = (RawComponent) obj;
|
||||
if (name == null) {
|
||||
if (other.name != null) return false;
|
||||
} else if (!name.equals(other.name)) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
package biweekly.component;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Defines the date range of a timezone's standard time.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Examples:</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* VTimezone timezone = new VTimezone("Eastern Standard Time");
|
||||
* StandardTime standard = new StandardTime();
|
||||
* DateTimeComponents components = new DateTimeComponents(1998, 10, 25, 2, 0, 0, false);
|
||||
* standard.setDateStart(components);
|
||||
* standard.setTimezoneOffsetFrom(-4, 0);
|
||||
* standard.setTimezoneOffsetTo(-5, 0);
|
||||
* timezone.addStandardTime(standard);
|
||||
* </pre>
|
||||
* @author Michael Angstadt
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-62">RFC 5545
|
||||
* p.62-71</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-60">RFC 2445 p.60-7</a>
|
||||
*/
|
||||
public class StandardTime extends Observance {
|
||||
public StandardTime() {
|
||||
//empty
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
* @param original the component to make a copy of
|
||||
*/
|
||||
public StandardTime(StandardTime original) {
|
||||
super(original);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StandardTime copy() {
|
||||
return new StandardTime(this);
|
||||
}
|
||||
}
|
@ -1,590 +0,0 @@
|
||||
package biweekly.component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.ValidationWarning;
|
||||
import biweekly.parameter.Related;
|
||||
import biweekly.property.Action;
|
||||
import biweekly.property.Attachment;
|
||||
import biweekly.property.Attendee;
|
||||
import biweekly.property.DateDue;
|
||||
import biweekly.property.DateEnd;
|
||||
import biweekly.property.DateStart;
|
||||
import biweekly.property.Description;
|
||||
import biweekly.property.DurationProperty;
|
||||
import biweekly.property.Repeat;
|
||||
import biweekly.property.Summary;
|
||||
import biweekly.property.Trigger;
|
||||
import biweekly.util.Duration;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Defines a reminder for an event or to-do task. This class contains static
|
||||
* factory methods to aid in the construction of valid alarms.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Examples:</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* //audio alarm
|
||||
* Trigger trigger = ...
|
||||
* Attachment sound = ...
|
||||
* VAlarm audio = VAlarm.audio(trigger, sound);
|
||||
*
|
||||
* //display alarm
|
||||
* Trigger trigger = ...
|
||||
* String message = "Meeting at 1pm";
|
||||
* VAlarm display = VAlarm.display(trigger, message);
|
||||
*
|
||||
* //email alarm
|
||||
* Trigger trigger = ...
|
||||
* String subject = "Reminder: Meeting at 1pm";
|
||||
* String body = "Team,\n\nThe team meeting scheduled for 1pm is about to start. Snacks will be served!\n\nThanks,\nJohn";
|
||||
* List<String> to = Arrays.asList("janedoe@example.com", "bobsmith@example.com");
|
||||
* VAlarm email = VAlarm.email(trigger, subject, body, to);
|
||||
* </pre>
|
||||
* @author Michael Angstadt
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-71">RFC 5545 p.71-6</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-67">RFC 2445
|
||||
* p.67-73</a>
|
||||
*/
|
||||
/*
|
||||
* Note: References to the vCal 1.0 spec are omitted from the property
|
||||
* getter/setter method Javadocs because vCal does not use the VALARM component.
|
||||
*/
|
||||
public class VAlarm extends ICalComponent {
|
||||
/**
|
||||
* Creates a new alarm. Consider using one of the static factory methods
|
||||
* instead.
|
||||
* @param action the alarm action (e.g. "email")
|
||||
* @param trigger the trigger
|
||||
*/
|
||||
public VAlarm(Action action, Trigger trigger) {
|
||||
setAction(action);
|
||||
setTrigger(trigger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
* @param original the component to make a copy of
|
||||
*/
|
||||
public VAlarm(VAlarm original) {
|
||||
super(original);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an audio alarm.
|
||||
* @param trigger the trigger
|
||||
* @return the alarm
|
||||
*/
|
||||
public static VAlarm audio(Trigger trigger) {
|
||||
return audio(trigger, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an audio alarm.
|
||||
* @param trigger the trigger
|
||||
* @param sound a sound to play when the alarm triggers
|
||||
* @return the alarm
|
||||
*/
|
||||
public static VAlarm audio(Trigger trigger, Attachment sound) {
|
||||
VAlarm alarm = new VAlarm(Action.audio(), trigger);
|
||||
if (sound != null) {
|
||||
alarm.addAttachment(sound);
|
||||
}
|
||||
return alarm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a display alarm.
|
||||
* @param trigger the trigger
|
||||
* @param displayText the display text
|
||||
* @return the alarm
|
||||
*/
|
||||
public static VAlarm display(Trigger trigger, String displayText) {
|
||||
VAlarm alarm = new VAlarm(Action.display(), trigger);
|
||||
alarm.setDescription(displayText);
|
||||
return alarm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an email alarm.
|
||||
* @param trigger the trigger
|
||||
* @param subject the email subject
|
||||
* @param body the email body
|
||||
* @param recipients the email address(es) to send the alert to
|
||||
* @return the alarm
|
||||
*/
|
||||
public static VAlarm email(Trigger trigger, String subject, String body, String... recipients) {
|
||||
return email(trigger, subject, body, Arrays.asList(recipients));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a procedure alarm (vCal 1.0 only).
|
||||
* @param trigger the trigger
|
||||
* @param path the path or name of the procedure
|
||||
* @return the alarm
|
||||
*/
|
||||
public static VAlarm procedure(Trigger trigger, String path) {
|
||||
VAlarm alarm = new VAlarm(Action.procedure(), trigger);
|
||||
alarm.setDescription(path);
|
||||
return alarm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an email alarm.
|
||||
* @param trigger the trigger
|
||||
* @param subject the email subject
|
||||
* @param body the email body
|
||||
* @param recipients the email address(es) to send the alert to
|
||||
* @return the alarm
|
||||
*/
|
||||
public static VAlarm email(Trigger trigger, String subject, String body, List<String> recipients) {
|
||||
VAlarm alarm = new VAlarm(Action.email(), trigger);
|
||||
alarm.setSummary(subject);
|
||||
alarm.setDescription(body);
|
||||
for (String recipient : recipients) {
|
||||
alarm.addAttendee(new Attendee(null, recipient));
|
||||
}
|
||||
return alarm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets any attachments that are associated with the alarm.
|
||||
* @return the attachments (any changes made this list will affect the
|
||||
* parent component object and vice versa)
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-80">RFC 5545
|
||||
* p.80-1</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-77">RFC 2445
|
||||
* p.77-8</a>
|
||||
*/
|
||||
public List<Attachment> getAttachments() {
|
||||
return getProperties(Attachment.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attachment to the alarm. Note that AUDIO alarms should only have
|
||||
* 1 attachment.
|
||||
* @param attachment the attachment to add
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-80">RFC 5545
|
||||
* p.80-1</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-77">RFC 2445
|
||||
* p.77-8</a>
|
||||
*/
|
||||
public void addAttachment(Attachment attachment) {
|
||||
addProperty(attachment);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gets a detailed description of the alarm. The description should be more
|
||||
* detailed than the one provided by the {@link Summary} property.
|
||||
* </p>
|
||||
* <p>
|
||||
* This property has different meanings, depending on the alarm action:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>DISPLAY - the display text</li>
|
||||
* <li>EMAIL - the body of the email message</li>
|
||||
* <li>all others - a general description of the alarm</li>
|
||||
* </ul>
|
||||
* @return the description or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-84">RFC 5545
|
||||
* p.84-5</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-81">RFC 2445
|
||||
* p.81-2</a>
|
||||
*/
|
||||
public Description getDescription() {
|
||||
return getProperty(Description.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets a detailed description of the alarm. The description should be more
|
||||
* detailed than the one provided by the {@link Summary} property.
|
||||
* </p>
|
||||
* <p>
|
||||
* This property has different meanings, depending on the alarm action:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>DISPLAY - the display text</li>
|
||||
* <li>EMAIL - the body of the email message</li>
|
||||
* <li>all others - a general description of the alarm</li>
|
||||
* </ul>
|
||||
* @param description the description or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-84">RFC 5545
|
||||
* p.84-5</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-81">RFC 2445
|
||||
* p.81-2</a>
|
||||
*/
|
||||
public void setDescription(Description description) {
|
||||
setProperty(Description.class, description);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets a detailed description of the alarm. The description should be more
|
||||
* detailed than the one provided by the {@link Summary} property.
|
||||
* </p>
|
||||
* <p>
|
||||
* This property has different meanings, depending on the alarm action:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>DISPLAY - the display text</li>
|
||||
* <li>EMAIL - the body of the email message</li>
|
||||
* <li>all others - a general description of the alarm</li>
|
||||
* </ul>
|
||||
* @param description the description or null to remove
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-84">RFC 5545
|
||||
* p.84-5</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-81">RFC 2445
|
||||
* p.81-2</a>
|
||||
*/
|
||||
public Description setDescription(String description) {
|
||||
Description prop = (description == null) ? null : new Description(description);
|
||||
setDescription(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gets the summary of the alarm.
|
||||
* </p>
|
||||
* <p>
|
||||
* This property has different meanings, depending on the alarm action:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>EMAIL - the subject line of the email</li>
|
||||
* <li>all others - a one-line summary of the alarm</li>
|
||||
* </ul>
|
||||
* @return the summary or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-93">RFC 5545
|
||||
* p.93-4</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-89">RFC 2445
|
||||
* p.89-90</a>
|
||||
*/
|
||||
public Summary getSummary() {
|
||||
return getProperty(Summary.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets the summary of the alarm.
|
||||
* </p>
|
||||
* <p>
|
||||
* This property has different meanings, depending on the alarm action:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>EMAIL - the subject line of the email</li>
|
||||
* <li>all others - a one-line summary of the alarm</li>
|
||||
* </ul>
|
||||
* @param summary the summary or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-93">RFC 5545
|
||||
* p.93-4</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-89">RFC 2445
|
||||
* p.89-90</a>
|
||||
*/
|
||||
public void setSummary(Summary summary) {
|
||||
setProperty(Summary.class, summary);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets the summary of the alarm.
|
||||
* </p>
|
||||
* <p>
|
||||
* This property has different meanings, depending on the alarm action:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>EMAIL - the subject line of the email</li>
|
||||
* <li>all others - a one-line summary of the alarm</li>
|
||||
* </ul>
|
||||
* @param summary the summary or null to remove
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-93">RFC 5545
|
||||
* p.93-4</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-89">RFC 2445
|
||||
* p.89-90</a>
|
||||
*/
|
||||
public Summary setSummary(String summary) {
|
||||
Summary prop = (summary == null) ? null : new Summary(summary);
|
||||
setSummary(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the people who will be emailed when the alarm fires (only applicable
|
||||
* for EMAIL alarms).
|
||||
* @return the email recipients (any changes made this list will affect the
|
||||
* parent component object and vice versa)
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-107">RFC 5545
|
||||
* p.107-9</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-102">RFC 2445
|
||||
* p.102-4</a>
|
||||
*/
|
||||
public List<Attendee> getAttendees() {
|
||||
return getProperties(Attendee.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a person who will be emailed when the alarm fires (only applicable
|
||||
* for EMAIL alarms).
|
||||
* @param attendee the email recipient
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-107">RFC 5545
|
||||
* p.107-9</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-102">RFC 2445
|
||||
* p.102-4</a>
|
||||
*/
|
||||
public void addAttendee(Attendee attendee) {
|
||||
addProperty(attendee);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of action to invoke when the alarm is triggered.
|
||||
* @return the action or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-132">RFC 5545
|
||||
* p.132-3</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-126">RFC 2445
|
||||
* p.126</a>
|
||||
*/
|
||||
public Action getAction() {
|
||||
return getProperty(Action.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of action to invoke when the alarm is triggered.
|
||||
* @param action the action or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-132">RFC 5545
|
||||
* p.132-3</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-126">RFC 2445
|
||||
* p.126</a>
|
||||
*/
|
||||
public void setAction(Action action) {
|
||||
setProperty(Action.class, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the length of the pause between alarm repetitions.
|
||||
* @return the duration or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-99">RFC 5545
|
||||
* p.99</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-94">RFC 2445
|
||||
* p.94-5</a>
|
||||
*/
|
||||
public DurationProperty getDuration() {
|
||||
return getProperty(DurationProperty.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the length of the pause between alarm repetitions.
|
||||
* @param duration the duration or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-99">RFC 5545
|
||||
* p.99</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-94">RFC 2445
|
||||
* p.94-5</a>
|
||||
*/
|
||||
public void setDuration(DurationProperty duration) {
|
||||
setProperty(DurationProperty.class, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the length of the pause between alarm repetitions.
|
||||
* @param duration the duration or null to remove
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-99">RFC 5545
|
||||
* p.99</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-94">RFC 2445
|
||||
* p.94-5</a>
|
||||
*/
|
||||
public DurationProperty setDuration(Duration duration) {
|
||||
DurationProperty prop = (duration == null) ? null : new DurationProperty(duration);
|
||||
setDuration(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of times an alarm should be repeated after its initial
|
||||
* trigger.
|
||||
* @return the repeat count or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-133">RFC 5545
|
||||
* p.133</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-126">RFC 2445
|
||||
* p.126-7</a>
|
||||
*/
|
||||
public Repeat getRepeat() {
|
||||
return getProperty(Repeat.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of times an alarm should be repeated after its initial
|
||||
* trigger.
|
||||
* @param repeat the repeat count or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-133">RFC 5545
|
||||
* p.133</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-126">RFC 2445
|
||||
* p.126-7</a>
|
||||
*/
|
||||
public void setRepeat(Repeat repeat) {
|
||||
setProperty(Repeat.class, repeat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of times an alarm should be repeated after its initial
|
||||
* trigger.
|
||||
* @param count the repeat count (e.g. "2" to repeat it two more times after
|
||||
* it was initially triggered, for a total of three times) or null to remove
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-133">RFC 5545
|
||||
* p.133</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-126">RFC 2445
|
||||
* p.126-7</a>
|
||||
*/
|
||||
public Repeat setRepeat(Integer count) {
|
||||
Repeat prop = (count == null) ? null : new Repeat(count);
|
||||
setRepeat(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the repetition information for the alarm.
|
||||
* @param count the repeat count (e.g. "2" to repeat it two more times after
|
||||
* it was initially triggered, for a total of three times)
|
||||
* @param pauseDuration the length of the pause between repeats
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-133">RFC 5545
|
||||
* p.133</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-126">RFC 2445
|
||||
* p.126-7</a>
|
||||
*/
|
||||
public void setRepeat(int count, Duration pauseDuration) {
|
||||
Repeat repeat = new Repeat(count);
|
||||
DurationProperty duration = new DurationProperty(pauseDuration);
|
||||
setRepeat(repeat);
|
||||
setDuration(duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets when the alarm will be triggered.
|
||||
* @return the trigger time or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-133">RFC 5545
|
||||
* p.133-6</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-127">RFC 2445
|
||||
* p.127-9</a>
|
||||
*/
|
||||
public Trigger getTrigger() {
|
||||
return getProperty(Trigger.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets when the alarm will be triggered.
|
||||
* @param trigger the trigger time or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-133">RFC 5545
|
||||
* p.133-6</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-127">RFC 2445
|
||||
* p.127-9</a>
|
||||
*/
|
||||
public void setTrigger(Trigger trigger) {
|
||||
setProperty(Trigger.class, trigger);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void validate(List<ICalComponent> components, ICalVersion version, List<ValidationWarning> warnings) {
|
||||
checkRequiredCardinality(warnings, Action.class, Trigger.class);
|
||||
|
||||
Action action = getAction();
|
||||
if (action != null) {
|
||||
//AUDIO alarms should not have more than 1 attachment
|
||||
if (action.isAudio()) {
|
||||
if (getAttachments().size() > 1) {
|
||||
warnings.add(new ValidationWarning(7));
|
||||
}
|
||||
}
|
||||
|
||||
//DESCRIPTION is required for DISPLAY alarms
|
||||
if (action.isDisplay()) {
|
||||
checkRequiredCardinality(warnings, Description.class);
|
||||
}
|
||||
|
||||
if (action.isEmail()) {
|
||||
//SUMMARY and DESCRIPTION is required for EMAIL alarms
|
||||
checkRequiredCardinality(warnings, Summary.class, Description.class);
|
||||
|
||||
//EMAIL alarms must have at least 1 ATTENDEE
|
||||
if (getAttendees().isEmpty()) {
|
||||
warnings.add(new ValidationWarning(8));
|
||||
}
|
||||
} else {
|
||||
//only EMAIL alarms can have ATTENDEEs
|
||||
if (!getAttendees().isEmpty()) {
|
||||
warnings.add(new ValidationWarning(9));
|
||||
}
|
||||
}
|
||||
|
||||
if (action.isProcedure()) {
|
||||
checkRequiredCardinality(warnings, Description.class);
|
||||
}
|
||||
}
|
||||
|
||||
Trigger trigger = getTrigger();
|
||||
if (trigger != null) {
|
||||
Related related = trigger.getRelated();
|
||||
if (related != null) {
|
||||
ICalComponent parent = components.get(components.size() - 1);
|
||||
|
||||
//if the TRIGGER is relative to DTSTART, confirm that DTSTART exists
|
||||
if (related == Related.START && parent.getProperty(DateStart.class) == null) {
|
||||
warnings.add(new ValidationWarning(11));
|
||||
}
|
||||
|
||||
//if the TRIGGER is relative to DTEND, confirm that DTEND (or DUE) exists
|
||||
if (related == Related.END) {
|
||||
boolean noEndDate = false;
|
||||
|
||||
if (parent instanceof VEvent) {
|
||||
noEndDate = (parent.getProperty(DateEnd.class) == null && (parent.getProperty(DateStart.class) == null || parent.getProperty(DurationProperty.class) == null));
|
||||
} else if (parent instanceof VTodo) {
|
||||
noEndDate = (parent.getProperty(DateDue.class) == null && (parent.getProperty(DateStart.class) == null || parent.getProperty(DurationProperty.class) == null));
|
||||
}
|
||||
|
||||
if (noEndDate) {
|
||||
warnings.add(new ValidationWarning(12));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public VAlarm copy() {
|
||||
return new VAlarm(this);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,655 +0,0 @@
|
||||
package biweekly.component;
|
||||
|
||||
import static biweekly.property.ValuedProperty.getValue;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.ValidationWarning;
|
||||
import biweekly.parameter.FreeBusyType;
|
||||
import biweekly.property.Attendee;
|
||||
import biweekly.property.Comment;
|
||||
import biweekly.property.Contact;
|
||||
import biweekly.property.DateEnd;
|
||||
import biweekly.property.DateStart;
|
||||
import biweekly.property.DateTimeStamp;
|
||||
import biweekly.property.FreeBusy;
|
||||
import biweekly.property.LastModified;
|
||||
import biweekly.property.Method;
|
||||
import biweekly.property.Organizer;
|
||||
import biweekly.property.RequestStatus;
|
||||
import biweekly.property.Uid;
|
||||
import biweekly.property.Url;
|
||||
import biweekly.util.Duration;
|
||||
import biweekly.util.ICalDate;
|
||||
import biweekly.util.Period;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Defines a collection of time ranges that describe when a person is available
|
||||
* and unavailable.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Examples:</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* VFreeBusy freebusy = new VFreeBusy();
|
||||
*
|
||||
* Date start = ...
|
||||
* Date end = ...
|
||||
* freebusy.addFreeBusy(FreeBusyType.FREE, start, end);
|
||||
*
|
||||
* start = ...
|
||||
* Duration duration = Duration.builder().hours(2).build();
|
||||
* freebusy.addFreeBusy(FreeBusyType.BUSY, start, duration);
|
||||
* </pre>
|
||||
* @author Michael Angstadt
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-59">RFC 5545
|
||||
* p.59-62</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-58">RFC 2445
|
||||
* p.58-60</a>
|
||||
*/
|
||||
/*
|
||||
* Note: References to the vCal 1.0 spec are omitted from the property
|
||||
* getter/setter method Javadocs because vCal does not use the VFREEBUSY
|
||||
* component.
|
||||
*/
|
||||
public class VFreeBusy extends ICalComponent {
|
||||
/**
|
||||
* <p>
|
||||
* Creates a new free/busy component.
|
||||
* </p>
|
||||
* <p>
|
||||
* The following properties are added to the component when it is created:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>{@link Uid}: Set to a UUID.</li>
|
||||
* <li>{@link DateTimeStamp}: Set to the current time.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public VFreeBusy() {
|
||||
setUid(Uid.random());
|
||||
setDateTimeStamp(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
* @param original the component to make a copy of
|
||||
*/
|
||||
public VFreeBusy(VFreeBusy original) {
|
||||
super(original);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unique identifier for this free/busy entry. This component
|
||||
* object comes populated with a UID on creation. This is a <b>required</b>
|
||||
* property.
|
||||
* @return the UID or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-117">RFC 5545
|
||||
* p.117-8</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-111">RFC 2445
|
||||
* p.111-2</a>
|
||||
*/
|
||||
public Uid getUid() {
|
||||
return getProperty(Uid.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the unique identifier for this free/busy entry. This component
|
||||
* object comes populated with a UID on creation. This is a <b>required</b>
|
||||
* property.
|
||||
* @param uid the UID or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-117">RFC 5545
|
||||
* p.117-8</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-111">RFC 2445
|
||||
* p.111-2</a>
|
||||
*/
|
||||
public void setUid(Uid uid) {
|
||||
setProperty(Uid.class, uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the unique identifier for this free/busy entry. This component
|
||||
* object comes populated with a UID on creation. This is a <b>required</b>
|
||||
* property.
|
||||
* @param uid the UID or null to remove
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-117">RFC 5545
|
||||
* p.117-8</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-111">RFC 2445
|
||||
* p.111-2</a>
|
||||
*/
|
||||
public Uid setUid(String uid) {
|
||||
Uid prop = (uid == null) ? null : new Uid(uid);
|
||||
setUid(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets either (a) the creation date of the iCalendar object (if the
|
||||
* {@link Method} property is defined) or (b) the date that the free/busy
|
||||
* entry was last modified (the {@link LastModified} property also holds
|
||||
* this information). This free/busy object comes populated with a
|
||||
* {@link DateTimeStamp} property that is set to the current time. This is a
|
||||
* <b>required</b> property.
|
||||
* @return the date time stamp or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-137">RFC 5545
|
||||
* p.137-8</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-130">RFC 2445
|
||||
* p.130-1</a>
|
||||
*/
|
||||
public DateTimeStamp getDateTimeStamp() {
|
||||
return getProperty(DateTimeStamp.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets either (a) the creation date of the iCalendar object (if the
|
||||
* {@link Method} property is defined) or (b) the date that the free/busy
|
||||
* entry was last modified (the {@link LastModified} property also holds
|
||||
* this information). This free/busy object comes populated with a
|
||||
* {@link DateTimeStamp} property that is set to the current time. This is a
|
||||
* <b>required</b> property.
|
||||
* @param dateTimeStamp the date time stamp or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-137">RFC 5545
|
||||
* p.137-8</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-130">RFC 2445
|
||||
* p.130-1</a>
|
||||
*/
|
||||
public void setDateTimeStamp(DateTimeStamp dateTimeStamp) {
|
||||
setProperty(DateTimeStamp.class, dateTimeStamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets either (a) the creation date of the iCalendar object (if the
|
||||
* {@link Method} property is defined) or (b) the date that the free/busy
|
||||
* entry was last modified (the {@link LastModified} property also holds
|
||||
* this information). This free/busy object comes populated with a
|
||||
* {@link DateTimeStamp} property that is set to the current time. This is a
|
||||
* <b>required</b> property.
|
||||
* @param dateTimeStamp the date time stamp or null to remove
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-137">RFC 5545
|
||||
* p.137-8</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-130">RFC 2445
|
||||
* p.130-1</a>
|
||||
*/
|
||||
public DateTimeStamp setDateTimeStamp(Date dateTimeStamp) {
|
||||
DateTimeStamp prop = (dateTimeStamp == null) ? null : new DateTimeStamp(dateTimeStamp);
|
||||
setDateTimeStamp(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the contact associated with the free/busy entry.
|
||||
* @return the contact or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-109">RFC 5545
|
||||
* p.109-11</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-104">RFC 2445
|
||||
* p.104-6</a>
|
||||
*/
|
||||
public Contact getContact() {
|
||||
return getProperty(Contact.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the contact for the free/busy entry.
|
||||
* @param contact the contact or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-109">RFC 5545
|
||||
* p.109-11</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-104">RFC 2445
|
||||
* p.104-6</a>
|
||||
*/
|
||||
public void setContact(Contact contact) {
|
||||
setProperty(Contact.class, contact);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the contact for the free/busy entry.
|
||||
* @param contact the contact (e.g. "ACME Co - (123) 555-1234")
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-109">RFC 5545
|
||||
* p.109-11</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-104">RFC 2445
|
||||
* p.104-6</a>
|
||||
*/
|
||||
public Contact addContact(String contact) {
|
||||
Contact prop = new Contact(contact);
|
||||
setContact(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date that the free/busy entry starts.
|
||||
* @return the start date or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-97">RFC 5545
|
||||
* p.97-8</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-93">RFC 2445
|
||||
* p.93-4</a>
|
||||
*/
|
||||
public DateStart getDateStart() {
|
||||
return getProperty(DateStart.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date that the free/busy entry starts.
|
||||
* @param dateStart the start date or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-97">RFC 5545
|
||||
* p.97-8</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-93">RFC 2445
|
||||
* p.93-4</a>
|
||||
*/
|
||||
public void setDateStart(DateStart dateStart) {
|
||||
setProperty(DateStart.class, dateStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date that the free/busy entry starts.
|
||||
* @param dateStart the start date or null to remove
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-97">RFC 5545
|
||||
* p.97-8</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-93">RFC 2445
|
||||
* p.93-4</a>
|
||||
*/
|
||||
public DateStart setDateStart(Date dateStart) {
|
||||
return setDateStart(dateStart, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date that the free/busy entry starts.
|
||||
* @param dateStart the start date or null to remove
|
||||
* @param hasTime true if the date has a time component, false if it is
|
||||
* strictly a date (if false, the given Date object should be created by a
|
||||
* {@link java.util.Calendar Calendar} object that uses the JVM's default
|
||||
* timezone)
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-97">RFC 5545
|
||||
* p.97-8</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-93">RFC 2445
|
||||
* p.93-4</a>
|
||||
*/
|
||||
public DateStart setDateStart(Date dateStart, boolean hasTime) {
|
||||
DateStart prop = (dateStart == null) ? null : new DateStart(dateStart, hasTime);
|
||||
setDateStart(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date that the free/busy entry ends.
|
||||
* @return the end date or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-95">RFC 5545
|
||||
* p.95-6</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-91">RFC 2445
|
||||
* p.91-2</a>
|
||||
*/
|
||||
public DateEnd getDateEnd() {
|
||||
return getProperty(DateEnd.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date that the free/busy entry ends.
|
||||
* @param dateEnd the end date or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-95">RFC 5545
|
||||
* p.95-6</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-91">RFC 2445
|
||||
* p.91-2</a>
|
||||
*/
|
||||
public void setDateEnd(DateEnd dateEnd) {
|
||||
setProperty(DateEnd.class, dateEnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date that the free/busy entry ends.
|
||||
* @param dateEnd the end date or null to remove
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-95">RFC 5545
|
||||
* p.95-6</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-91">RFC 2445
|
||||
* p.91-2</a>
|
||||
*/
|
||||
public DateEnd setDateEnd(Date dateEnd) {
|
||||
return setDateEnd(dateEnd, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date that the free/busy entry ends.
|
||||
* @param dateEnd the end date or null to remove
|
||||
* @param hasTime true if the date has a time component, false if it is
|
||||
* strictly a date (if false, the given Date object should be created by a
|
||||
* {@link java.util.Calendar Calendar} object that uses the JVM's default
|
||||
* timezone)
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-95">RFC 5545
|
||||
* p.95-6</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-91">RFC 2445
|
||||
* p.91-2</a>
|
||||
*/
|
||||
public DateEnd setDateEnd(Date dateEnd, boolean hasTime) {
|
||||
DateEnd prop = (dateEnd == null) ? null : new DateEnd(dateEnd, hasTime);
|
||||
setDateEnd(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the person requesting the free/busy time.
|
||||
* @return the person requesting the free/busy time or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-111">RFC 5545
|
||||
* p.111-2</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-106">RFC 2445
|
||||
* p.106-7</a>
|
||||
*/
|
||||
public Organizer getOrganizer() {
|
||||
return getProperty(Organizer.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the person requesting the free/busy time.
|
||||
* @param organizer the person requesting the free/busy time or null to
|
||||
* remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-111">RFC 5545
|
||||
* p.111-2</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-106">RFC 2445
|
||||
* p.106-7</a>
|
||||
*/
|
||||
public void setOrganizer(Organizer organizer) {
|
||||
setProperty(Organizer.class, organizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the person requesting the free/busy time.
|
||||
* @param email the email address of the person requesting the free/busy
|
||||
* time (e.g. "johndoe@example.com") or null to remove
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-111">RFC 5545
|
||||
* p.111-2</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-106">RFC 2445
|
||||
* p.106-7</a>
|
||||
*/
|
||||
public Organizer setOrganizer(String email) {
|
||||
Organizer prop = (email == null) ? null : new Organizer(null, email);
|
||||
setOrganizer(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a URL to a resource that contains additional information about the
|
||||
* free/busy entry.
|
||||
* @return the URL or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-116">RFC 5545
|
||||
* p.116-7</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-110">RFC 2445
|
||||
* p.110-1</a>
|
||||
*/
|
||||
public Url getUrl() {
|
||||
return getProperty(Url.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a URL to a resource that contains additional information about the
|
||||
* free/busy entry.
|
||||
* @param url the URL or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-116">RFC 5545
|
||||
* p.116-7</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-110">RFC 2445
|
||||
* p.110-1</a>
|
||||
*/
|
||||
public void setUrl(Url url) {
|
||||
setProperty(Url.class, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a URL to a resource that contains additional information about the
|
||||
* free/busy entry.
|
||||
* @param url the URL (e.g. "http://example.com/resource.ics") or null to
|
||||
* remove
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-116">RFC 5545
|
||||
* p.116-7</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-110">RFC 2445
|
||||
* p.110-1</a>
|
||||
*/
|
||||
public Url setUrl(String url) {
|
||||
Url prop = (url == null) ? null : new Url(url);
|
||||
setUrl(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the people who are involved in the free/busy entry.
|
||||
* @return the attendees (any changes made this list will affect the parent
|
||||
* component object and vice versa)
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-107">RFC 5545
|
||||
* p.107-9</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-102">RFC 2445
|
||||
* p.102-4</a>
|
||||
*/
|
||||
public List<Attendee> getAttendees() {
|
||||
return getProperties(Attendee.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a person who is involved in the free/busy entry.
|
||||
* @param attendee the attendee
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-107">RFC 5545
|
||||
* p.107-9</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-102">RFC 2445
|
||||
* p.102-4</a>
|
||||
*/
|
||||
public void addAttendee(Attendee attendee) {
|
||||
addProperty(attendee);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the comments attached to the free/busy entry.
|
||||
* @return the comments (any changes made this list will affect the parent
|
||||
* component object and vice versa)
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-83">RFC 5545
|
||||
* p.83-4</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-80">RFC 2445
|
||||
* p.80-1</a>
|
||||
*/
|
||||
public List<Comment> getComments() {
|
||||
return getProperties(Comment.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a comment to the free/busy entry.
|
||||
* @param comment the comment to add
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-83">RFC 5545
|
||||
* p.83-4</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-80">RFC 2445
|
||||
* p.80-1</a>
|
||||
*/
|
||||
public void addComment(Comment comment) {
|
||||
addProperty(comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a comment to the free/busy entry.
|
||||
* @param comment the comment to add
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-83">RFC 5545
|
||||
* p.83-4</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-80">RFC 2445
|
||||
* p.80-1</a>
|
||||
*/
|
||||
public Comment addComment(String comment) {
|
||||
Comment prop = new Comment(comment);
|
||||
addComment(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the person's availabilities over certain time periods (for example,
|
||||
* "free" between 1pm-3pm, but "busy" between 3pm-4pm).
|
||||
* @return the availabilities (any changes made this list will affect the
|
||||
* parent component object and vice versa)
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-100">RFC 5545
|
||||
* p.100-1</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-95">RFC 2445
|
||||
* p.95-6</a>
|
||||
*/
|
||||
public List<FreeBusy> getFreeBusy() {
|
||||
return getProperties(FreeBusy.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a list of time periods for which the person is free or busy (for
|
||||
* example, "free" between 1pm-3pm and 4pm-5pm). Note that a
|
||||
* {@link FreeBusy} property can contain multiple time periods, but only one
|
||||
* availability type (e.g. "busy").
|
||||
* @param freeBusy the availabilities
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-100">RFC 5545
|
||||
* p.100-1</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-95">RFC 2445
|
||||
* p.95-6</a>
|
||||
*/
|
||||
public void addFreeBusy(FreeBusy freeBusy) {
|
||||
addProperty(freeBusy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a single time period for which the person is free or busy (for
|
||||
* example, "free" between 1pm-3pm). This method will look for an existing
|
||||
* property that has the given {@link FreeBusyType} and add the time period
|
||||
* to it, or create a new property is one cannot be found.
|
||||
* @param type the availability type (e.g. "free" or "busy")
|
||||
* @param start the start date-time
|
||||
* @param end the end date-time
|
||||
* @return the property that was created/modified
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-100">RFC 5545
|
||||
* p.100-1</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-95">RFC 2445
|
||||
* p.95-6</a>
|
||||
*/
|
||||
public FreeBusy addFreeBusy(FreeBusyType type, Date start, Date end) {
|
||||
FreeBusy found = findByFreeBusyType(type);
|
||||
found.getValues().add(new Period(start, end));
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a single time period for which the person is free or busy (for
|
||||
* example, "free" for 2 hours after 1pm). This method will look for an
|
||||
* existing property that has the given {@link FreeBusyType} and add the
|
||||
* time period to it, or create a new property is one cannot be found.
|
||||
* @param type the availability type (e.g. "free" or "busy")
|
||||
* @param start the start date-time
|
||||
* @param duration the length of time
|
||||
* @return the property that was created/modified
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-100">RFC 5545
|
||||
* p.100-1</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-95">RFC 2445
|
||||
* p.95-6</a>
|
||||
*/
|
||||
public FreeBusy addFreeBusy(FreeBusyType type, Date start, Duration duration) {
|
||||
FreeBusy found = findByFreeBusyType(type);
|
||||
found.getValues().add(new Period(start, duration));
|
||||
return found;
|
||||
}
|
||||
|
||||
private FreeBusy findByFreeBusyType(FreeBusyType type) {
|
||||
for (FreeBusy freeBusy : getFreeBusy()) {
|
||||
if (freeBusy.getType() == type) {
|
||||
return freeBusy;
|
||||
}
|
||||
}
|
||||
|
||||
FreeBusy freeBusy = new FreeBusy();
|
||||
freeBusy.setType(type);
|
||||
addFreeBusy(freeBusy);
|
||||
return freeBusy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the response to a scheduling request.
|
||||
* @return the response
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5546#section-3.6">RFC 5546
|
||||
* Section 3.6</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-141">RFC 5545
|
||||
* p.141-3</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-134">RFC 2445
|
||||
* p.134-6</a>
|
||||
*/
|
||||
public RequestStatus getRequestStatus() {
|
||||
return getProperty(RequestStatus.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the response to a scheduling request.
|
||||
* @param requestStatus the response
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5546#section-3.6">RFC 5546
|
||||
* Section 3.6</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-141">RFC 5545
|
||||
* p.141-3</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-134">RFC 2445
|
||||
* p.134-6</a>
|
||||
*/
|
||||
public void setRequestStatus(RequestStatus requestStatus) {
|
||||
setProperty(RequestStatus.class, requestStatus);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void validate(List<ICalComponent> components, ICalVersion version, List<ValidationWarning> warnings) {
|
||||
if (version == ICalVersion.V1_0) {
|
||||
warnings.add(new ValidationWarning(48, version));
|
||||
}
|
||||
|
||||
checkRequiredCardinality(warnings, Uid.class, DateTimeStamp.class);
|
||||
checkOptionalCardinality(warnings, Contact.class, DateStart.class, DateEnd.class, Organizer.class, Url.class);
|
||||
|
||||
ICalDate dateStart = getValue(getDateStart());
|
||||
ICalDate dateEnd = getValue(getDateEnd());
|
||||
|
||||
//DTSTART is required if DTEND exists
|
||||
if (dateEnd != null && dateStart == null) {
|
||||
warnings.add(new ValidationWarning(15));
|
||||
}
|
||||
|
||||
//DTSTART and DTEND must contain a time component
|
||||
if (dateStart != null && !dateStart.hasTime()) {
|
||||
warnings.add(new ValidationWarning(20, DateStart.class.getSimpleName()));
|
||||
}
|
||||
if (dateEnd != null && !dateEnd.hasTime()) {
|
||||
warnings.add(new ValidationWarning(20, DateEnd.class.getSimpleName()));
|
||||
}
|
||||
|
||||
//DTSTART must come before DTEND
|
||||
if (dateStart != null && dateEnd != null && dateStart.compareTo(dateEnd) >= 0) {
|
||||
warnings.add(new ValidationWarning(16));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public VFreeBusy copy() {
|
||||
return new VFreeBusy(this);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,282 +0,0 @@
|
||||
package biweekly.component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.ValidationWarning;
|
||||
import biweekly.property.LastModified;
|
||||
import biweekly.property.TimezoneId;
|
||||
import biweekly.property.TimezoneUrl;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Defines a timezone's UTC offsets throughout the year.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <b>Examples:</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* VTimezone timezone = new VTimezone("Eastern Standard Time");
|
||||
*
|
||||
* StandardTime standard = new StandardTime();
|
||||
* DateTimeComponents componentsStandard = new DateTimeComponents(1998, 10, 25, 2, 0, 0, false);
|
||||
* standard.setDateStart(componentsStandard);
|
||||
* standard.setTimezoneOffsetFrom(-4, 0);
|
||||
* standard.setTimezoneOffsetTo(-5, 0);
|
||||
* timezone.addStandardTime(standard);
|
||||
*
|
||||
* DaylightSavingsTime daylight = new DaylightSavingsTime();
|
||||
* DateTimeComponents componentsDaylight = new DateTimeComponents(1999, 4, 4, 2, 0, 0, false);
|
||||
* daylight.setDateStart(componentsDaylight);
|
||||
* daylight.setTimezoneOffsetFrom(-5, 0);
|
||||
* daylight.setTimezoneOffsetTo(-4, 0);
|
||||
* timezone.addDaylightSavingsTime(daylight);
|
||||
* </pre>
|
||||
* @author Michael Angstadt
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-62">RFC 5545
|
||||
* p.62-71</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-60">RFC 2445 p.60-7</a>
|
||||
*/
|
||||
/*
|
||||
* Note: References to the vCal 1.0 spec are omitted from the property
|
||||
* getter/setter method Javadocs because vCal does not use the VTIMEZONE
|
||||
* component.
|
||||
*/
|
||||
public class VTimezone extends ICalComponent {
|
||||
/**
|
||||
* Creates a new timezone component.
|
||||
* @param identifier a unique identifier for this timezone (allows it to be
|
||||
* referenced by date-time properties that support timezones).
|
||||
*/
|
||||
public VTimezone(String identifier) {
|
||||
setTimezoneId(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
* @param original the component to make a copy of
|
||||
*/
|
||||
public VTimezone(VTimezone original) {
|
||||
super(original);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ID for this timezone. This is a <b>required</b> property.
|
||||
* @return the timezone ID or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-102">RFC 5545
|
||||
* p.102-3</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-97">RFC 2445
|
||||
* p.97-8</a>
|
||||
*/
|
||||
public TimezoneId getTimezoneId() {
|
||||
return getProperty(TimezoneId.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an ID for this timezone. This is a <b>required</b> property.
|
||||
* @param timezoneId the timezone ID or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-102">RFC 5545
|
||||
* p.102-3</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-97">RFC 2445
|
||||
* p.97-8</a>
|
||||
*/
|
||||
public void setTimezoneId(TimezoneId timezoneId) {
|
||||
setProperty(TimezoneId.class, timezoneId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an ID for this timezone. This is a <b>required</b> property.
|
||||
* @param timezoneId the timezone ID or null to remove
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-102">RFC 5545
|
||||
* p.102-3</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-97">RFC 2445
|
||||
* p.97-8</a>
|
||||
*/
|
||||
public TimezoneId setTimezoneId(String timezoneId) {
|
||||
TimezoneId prop = (timezoneId == null) ? null : new TimezoneId(timezoneId);
|
||||
setTimezoneId(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date-time that the timezone data was last changed.
|
||||
* @return the last modified date or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-138">RFC 5545
|
||||
* p.138</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-131">RFC 2445
|
||||
* p.131</a>
|
||||
*/
|
||||
public LastModified getLastModified() {
|
||||
return getProperty(LastModified.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date-time that the timezone data was last changed.
|
||||
* @param lastModified the last modified date or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-138">RFC 5545
|
||||
* p.138</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-131">RFC 2445
|
||||
* p.131</a>
|
||||
*/
|
||||
public void setLastModified(LastModified lastModified) {
|
||||
setProperty(LastModified.class, lastModified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date-time that the timezone data was last changed.
|
||||
* @param lastModified the last modified date or null to remove
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-138">RFC 5545
|
||||
* p.138</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-131">RFC 2445
|
||||
* p.131</a>
|
||||
*/
|
||||
public LastModified setLastModified(Date lastModified) {
|
||||
LastModified prop = (lastModified == null) ? null : new LastModified(lastModified);
|
||||
setLastModified(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timezone URL, which points to an iCalendar object that contains
|
||||
* further information on the timezone.
|
||||
* @return the URL or null if not set
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-106">RFC 5545
|
||||
* p.106</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-101">RFC 2445
|
||||
* p.101</a>
|
||||
*/
|
||||
public TimezoneUrl getTimezoneUrl() {
|
||||
return getProperty(TimezoneUrl.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timezone URL, which points to an iCalendar object that contains
|
||||
* further information on the timezone.
|
||||
* @param url the URL or null to remove
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-106">RFC 5545
|
||||
* p.106</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-101">RFC 2445
|
||||
* p.101</a>
|
||||
*/
|
||||
public void setTimezoneUrl(TimezoneUrl url) {
|
||||
setProperty(TimezoneUrl.class, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timezone URL, which points to an iCalendar object that contains
|
||||
* further information on the timezone.
|
||||
* @param url the timezone URL (e.g.
|
||||
* "http://example.com/America-New_York.ics") or null to remove
|
||||
* @return the property that was created
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-106">RFC 5545
|
||||
* p.106</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-101">RFC 2445
|
||||
* p.101</a>
|
||||
*/
|
||||
public TimezoneUrl setTimezoneUrl(String url) {
|
||||
TimezoneUrl prop = (url == null) ? null : new TimezoneUrl(url);
|
||||
setTimezoneUrl(prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timezone's "standard" observance time ranges.
|
||||
* @return the "standard" observance time ranges (any changes made this list
|
||||
* will affect the parent component object and vice versa)
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-62">RFC 5545
|
||||
* p.62-71</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-60">RFC 2445
|
||||
* p.60-7</a>
|
||||
*/
|
||||
public List<StandardTime> getStandardTimes() {
|
||||
return getComponents(StandardTime.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a "standard" observance time range.
|
||||
* @param standardTime the "standard" observance time
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-62">RFC 5545
|
||||
* p.62-71</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-60">RFC 2445
|
||||
* p.60-7</a>
|
||||
*/
|
||||
public void addStandardTime(StandardTime standardTime) {
|
||||
addComponent(standardTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timezone's "daylight savings" observance time ranges.
|
||||
* @return the "daylight savings" observance time ranges (any changes made
|
||||
* this list will affect the parent component object and vice versa)
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-62">RFC 5545
|
||||
* p.62-71</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-60">RFC 2445
|
||||
* p.60-7</a>
|
||||
*/
|
||||
public List<DaylightSavingsTime> getDaylightSavingsTime() {
|
||||
return getComponents(DaylightSavingsTime.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a "daylight savings" observance time range.
|
||||
* @param daylightSavingsTime the "daylight savings" observance time
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5545#page-62">RFC 5545
|
||||
* p.62-71</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2445#page-60">RFC 2445
|
||||
* p.60-7</a>
|
||||
*/
|
||||
public void addDaylightSavingsTime(DaylightSavingsTime daylightSavingsTime) {
|
||||
addComponent(daylightSavingsTime);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void validate(List<ICalComponent> components, ICalVersion version, List<ValidationWarning> warnings) {
|
||||
if (version == ICalVersion.V1_0) {
|
||||
warnings.add(new ValidationWarning(48, version));
|
||||
}
|
||||
|
||||
checkRequiredCardinality(warnings, TimezoneId.class);
|
||||
checkOptionalCardinality(warnings, LastModified.class, TimezoneUrl.class);
|
||||
|
||||
//STANDARD or DAYLIGHT must be defined
|
||||
if (getStandardTimes().isEmpty() && getDaylightSavingsTime().isEmpty()) {
|
||||
warnings.add(new ValidationWarning(21));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public VTimezone copy() {
|
||||
return new VTimezone(this);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +0,0 @@
|
||||
/**
|
||||
* Contains DTO classes for each component.
|
||||
*/
|
||||
package biweekly.component;
|
@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
@ -1,79 +0,0 @@
|
||||
package biweekly.io;
|
||||
|
||||
import biweekly.Messages;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Thrown during the unmarshalling of an iCalendar property to signal that the
|
||||
* property's value could not be parsed (for example, being unable to parse a
|
||||
* date string).
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class CannotParseException extends RuntimeException {
|
||||
private static final long serialVersionUID = 8299420302297241326L;
|
||||
private final Integer code;
|
||||
private final Object args[];
|
||||
|
||||
/**
|
||||
* Creates a new "cannot parse" exception.
|
||||
* @param code the warning message code
|
||||
* @param args the warning message arguments
|
||||
*/
|
||||
public CannotParseException(int code, Object... args) {
|
||||
this.code = code;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new "cannot parse" exception.
|
||||
* @param reason the reason why the property value cannot be parsed
|
||||
*/
|
||||
public CannotParseException(String reason) {
|
||||
this(1, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the warning message code.
|
||||
* @return the message code
|
||||
*/
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the warning message arguments.
|
||||
* @return the message arguments
|
||||
*/
|
||||
public Object[] getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return Messages.INSTANCE.getParseMessage(code, args);
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
package biweekly.io;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import biweekly.component.ICalComponent;
|
||||
import biweekly.component.VAlarm;
|
||||
import biweekly.property.AudioAlarm;
|
||||
import biweekly.property.ICalProperty;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Thrown when a component or property needs to be converted to a different
|
||||
* component or property when being read or written. For example, converting a
|
||||
* vCal {@link AudioAlarm} property to a {@link VAlarm} component when parsing a
|
||||
* vCal file.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class DataModelConversionException extends RuntimeException {
|
||||
private static final long serialVersionUID = -4789186852509057375L;
|
||||
private final ICalProperty originalProperty;
|
||||
private final List<ICalComponent> components = new ArrayList<ICalComponent>();
|
||||
private final List<ICalProperty> properties = new ArrayList<ICalProperty>();
|
||||
|
||||
/**
|
||||
* Creates a conversion exception.
|
||||
* @param originalProperty the original property object that was parsed or
|
||||
* null if not applicable
|
||||
*/
|
||||
public DataModelConversionException(ICalProperty originalProperty) {
|
||||
this.originalProperty = originalProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the original property object that was parsed.
|
||||
* @return the original property object or null if not applicable
|
||||
*/
|
||||
public ICalProperty getOriginalProperty() {
|
||||
return originalProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the components that were converted from the original property.
|
||||
* @return the components
|
||||
*/
|
||||
public List<ICalComponent> getComponents() {
|
||||
return components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the properties that were converted from the original property.
|
||||
* @return the properties
|
||||
*/
|
||||
public List<ICalProperty> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
}
|
@ -1,241 +0,0 @@
|
||||
package biweekly.io;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import biweekly.component.DaylightSavingsTime;
|
||||
import biweekly.component.Observance;
|
||||
import biweekly.component.StandardTime;
|
||||
import biweekly.component.VTimezone;
|
||||
import biweekly.io.ICalTimeZone.Boundary;
|
||||
import biweekly.property.Daylight;
|
||||
import biweekly.property.Timezone;
|
||||
import biweekly.property.UtcOffsetProperty;
|
||||
import biweekly.property.ValuedProperty;
|
||||
import biweekly.util.DateTimeComponents;
|
||||
import biweekly.util.ICalDate;
|
||||
import biweekly.util.UtcOffset;
|
||||
import biweekly.util.com.google.ical.values.DateTimeValue;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Converts various properties/components into other properties/components for
|
||||
* vCalendar-iCalendar compatibility.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public final class DataModelConverter {
|
||||
/**
|
||||
* Converts vCalendar timezone information to an iCalendar {@link VTimezone}
|
||||
* component.
|
||||
* @param daylights the DAYLIGHT properties
|
||||
* @param tz the TZ property
|
||||
* @return the VTIMEZONE component
|
||||
*/
|
||||
public static VTimezone convert(List<Daylight> daylights, Timezone tz) {
|
||||
UtcOffset tzOffset = ValuedProperty.getValue(tz);
|
||||
if (daylights.isEmpty() && tzOffset == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
VTimezone timezone = new VTimezone("TZ");
|
||||
if (daylights.isEmpty() && tzOffset != null) {
|
||||
StandardTime st = new StandardTime();
|
||||
st.setTimezoneOffsetFrom(tzOffset);
|
||||
st.setTimezoneOffsetTo(tzOffset);
|
||||
timezone.addStandardTime(st);
|
||||
return timezone;
|
||||
}
|
||||
|
||||
for (Daylight daylight : daylights) {
|
||||
if (!daylight.isDaylight()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UtcOffset daylightOffset = daylight.getOffset();
|
||||
UtcOffset standardOffset = new UtcOffset(daylightOffset.getMillis() - (1000 * 60 * 60));
|
||||
|
||||
DaylightSavingsTime dst = new DaylightSavingsTime();
|
||||
dst.setDateStart(daylight.getStart());
|
||||
dst.setTimezoneOffsetFrom(standardOffset);
|
||||
dst.setTimezoneOffsetTo(daylightOffset);
|
||||
dst.addTimezoneName(daylight.getDaylightName());
|
||||
timezone.addDaylightSavingsTime(dst);
|
||||
|
||||
StandardTime st = new StandardTime();
|
||||
st.setDateStart(daylight.getEnd());
|
||||
st.setTimezoneOffsetFrom(daylightOffset);
|
||||
st.setTimezoneOffsetTo(standardOffset);
|
||||
st.addTimezoneName(daylight.getStandardName());
|
||||
timezone.addStandardTime(st);
|
||||
}
|
||||
|
||||
return timezone.getComponents().isEmpty() ? null : timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an iCalendar {@link VTimezone} component into the appropriate
|
||||
* vCalendar properties.
|
||||
* @param timezone the TIMEZONE component
|
||||
* @param dates the date values in the vCalendar object that are effected by
|
||||
* the timezone.
|
||||
* @return the vCalendar properties
|
||||
*/
|
||||
public static VCalTimezoneProperties convert(VTimezone timezone, List<Date> dates) {
|
||||
List<Daylight> daylights = new ArrayList<Daylight>();
|
||||
Timezone tz = null;
|
||||
if (dates.isEmpty()) {
|
||||
return new VCalTimezoneProperties(daylights, tz);
|
||||
}
|
||||
|
||||
ICalTimeZone icalTz = new ICalTimeZone(timezone);
|
||||
Collections.sort(dates);
|
||||
Set<DateTimeValue> daylightStartDates = new HashSet<DateTimeValue>();
|
||||
boolean zeroObservanceUsed = false;
|
||||
for (Date date : dates) {
|
||||
Boundary boundary = icalTz.getObservanceBoundary(date);
|
||||
Observance observance = boundary.getObservanceIn();
|
||||
Observance observanceAfter = boundary.getObservanceAfter();
|
||||
if (observance == null && observanceAfter == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (observance == null) {
|
||||
//the date comes before the earliest observance
|
||||
if (observanceAfter instanceof StandardTime && !zeroObservanceUsed) {
|
||||
UtcOffset offset = getOffset(observanceAfter.getTimezoneOffsetFrom());
|
||||
DateTimeValue start = null;
|
||||
DateTimeValue end = boundary.getObservanceAfterStart();
|
||||
String standardName = icalTz.getDisplayName(false, TimeZone.SHORT);
|
||||
String daylightName = icalTz.getDisplayName(true, TimeZone.SHORT);
|
||||
|
||||
Daylight daylight = new Daylight(true, offset, convert(start), convert(end), standardName, daylightName);
|
||||
daylights.add(daylight);
|
||||
zeroObservanceUsed = true;
|
||||
}
|
||||
|
||||
if (observanceAfter instanceof DaylightSavingsTime) {
|
||||
UtcOffset offset = getOffset(observanceAfter.getTimezoneOffsetFrom());
|
||||
if (offset != null) {
|
||||
tz = new Timezone(offset);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (observance instanceof StandardTime) {
|
||||
UtcOffset offset = getOffset(observance.getTimezoneOffsetTo());
|
||||
if (offset != null) {
|
||||
tz = new Timezone(offset);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (observance instanceof DaylightSavingsTime && !daylightStartDates.contains(boundary.getObservanceInStart())) {
|
||||
UtcOffset offset = getOffset(observance.getTimezoneOffsetTo());
|
||||
DateTimeValue start = boundary.getObservanceInStart();
|
||||
DateTimeValue end = null;
|
||||
if (observanceAfter != null) {
|
||||
end = boundary.getObservanceAfterStart();
|
||||
}
|
||||
|
||||
String standardName = icalTz.getDisplayName(false, TimeZone.SHORT);
|
||||
String daylightName = icalTz.getDisplayName(true, TimeZone.SHORT);
|
||||
|
||||
Daylight daylight = new Daylight(true, offset, convert(start), convert(end), standardName, daylightName);
|
||||
daylights.add(daylight);
|
||||
daylightStartDates.add(start);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (tz == null) {
|
||||
int rawOffset = icalTz.getRawOffset();
|
||||
UtcOffset offset = new UtcOffset(rawOffset);
|
||||
tz = new Timezone(offset);
|
||||
}
|
||||
|
||||
if (daylights.isEmpty()) {
|
||||
Daylight daylight = new Daylight();
|
||||
daylight.setDaylight(false);
|
||||
daylights.add(daylight);
|
||||
}
|
||||
|
||||
return new VCalTimezoneProperties(daylights, tz);
|
||||
}
|
||||
|
||||
private static UtcOffset getOffset(UtcOffsetProperty property) {
|
||||
return (property == null) ? null : property.getValue();
|
||||
}
|
||||
|
||||
private static ICalDate convert(DateTimeValue value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
DateTimeComponents components = new DateTimeComponents(
|
||||
value.year(),
|
||||
value.month(),
|
||||
value.day(),
|
||||
value.hour(),
|
||||
value.minute(),
|
||||
value.second(),
|
||||
false
|
||||
);
|
||||
//@formatter:on
|
||||
|
||||
return new ICalDate(components, true);
|
||||
}
|
||||
|
||||
public static class VCalTimezoneProperties {
|
||||
private final List<Daylight> daylights;
|
||||
private final Timezone tz;
|
||||
|
||||
public VCalTimezoneProperties(List<Daylight> daylights, Timezone tz) {
|
||||
this.daylights = daylights;
|
||||
this.tz = tz;
|
||||
}
|
||||
|
||||
public List<Daylight> getDaylights() {
|
||||
return daylights;
|
||||
}
|
||||
|
||||
public Timezone getTz() {
|
||||
return tz;
|
||||
}
|
||||
}
|
||||
|
||||
private DataModelConverter() {
|
||||
//hide
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
package biweekly.io;
|
||||
|
||||
import biweekly.util.ICalDateFormat;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Default implementation of {@link GlobalTimezoneIdResolver}.
|
||||
* </p>
|
||||
* <p>
|
||||
* The following are examples of the kinds of TZID formats this class is able to
|
||||
* handle.
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>"TZID=/America/New_York" resolves to
|
||||
* {@code TimeZone.getTimeZone("America/New_York")}</li>
|
||||
* <li>"TZID=/mozilla.org/20050126_1/America/New_York" resolves to
|
||||
* {@code TimeZone.getTimeZone("America/New_York")}</li>
|
||||
* </ul>
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class DefaultGlobalTimezoneIdResolver implements GlobalTimezoneIdResolver {
|
||||
@Override
|
||||
public TimeZone resolve(String globalId) {
|
||||
globalId = removeMozillaPrefixIfPresent(globalId);
|
||||
return ICalDateFormat.parseTimeZoneId(globalId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for, and removes, a global ID prefix that Mozilla software adds to
|
||||
* its iCal files. Googling this prefix returns several search results,
|
||||
* suggesting it is frequently encountered in the wild.
|
||||
* @param globalId the global ID (may or may not contain the Mozilla prefix)
|
||||
* @return the sanitized global ID, or the unchanged ID if it does not
|
||||
* contain the prefix
|
||||
*/
|
||||
private String removeMozillaPrefixIfPresent(String globalId) {
|
||||
String prefix = "mozilla.org/20050126_1/";
|
||||
return globalId.startsWith(prefix) ? globalId.substring(prefix.length()) : globalId;
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package biweekly.io;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets Java {@link TimeZone} objects that correspond with TZID parameters that
|
||||
* contain global timezone IDs (as opposed to IDs that correspond with a
|
||||
* VTIMEZONE component).
|
||||
* @author Michael Angstadt
|
||||
* @see <a href="https://tools.ietf.org/html/rfc5545#section-3.8.3.1">RFC 5545
|
||||
* section 3.8.3.1</a>
|
||||
* @see <a href="https://tools.ietf.org/html/rfc2445#section-4.2.19">RFC 2445
|
||||
* section 4.2.19</a>
|
||||
*/
|
||||
public interface GlobalTimezoneIdResolver {
|
||||
/**
|
||||
* Returns an appropriate Java {@link TimeZone} object that corresponds to
|
||||
* the given global ID.
|
||||
* @param globalId the global ID (the value of the TZID parameter, without
|
||||
* the forward slash prefix)
|
||||
* @return the corresponding {@link TimeZone} object or null if the global
|
||||
* ID is not recognized
|
||||
*/
|
||||
TimeZone resolve(String globalId);
|
||||
}
|
@ -1,720 +0,0 @@
|
||||
package biweekly.io;
|
||||
|
||||
import static biweekly.property.ValuedProperty.getValue;
|
||||
import static biweekly.util.Google2445Utils.convertFromRawComponents;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import biweekly.Messages;
|
||||
import biweekly.component.DaylightSavingsTime;
|
||||
import biweekly.component.Observance;
|
||||
import biweekly.component.StandardTime;
|
||||
import biweekly.component.VTimezone;
|
||||
import biweekly.property.ExceptionDates;
|
||||
import biweekly.property.ExceptionRule;
|
||||
import biweekly.property.RecurrenceDates;
|
||||
import biweekly.property.RecurrenceRule;
|
||||
import biweekly.property.TimezoneName;
|
||||
import biweekly.util.ICalDate;
|
||||
import biweekly.util.Recurrence;
|
||||
import biweekly.util.UtcOffset;
|
||||
import biweekly.util.com.google.ical.iter.RecurrenceIterator;
|
||||
import biweekly.util.com.google.ical.iter.RecurrenceIteratorFactory;
|
||||
import biweekly.util.com.google.ical.util.DTBuilder;
|
||||
import biweekly.util.com.google.ical.values.DateTimeValue;
|
||||
import biweekly.util.com.google.ical.values.DateTimeValueImpl;
|
||||
import biweekly.util.com.google.ical.values.DateValue;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A timezone that is based on an iCalendar {@link VTimezone} component. This
|
||||
* class is not thread safe.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ICalTimeZone extends TimeZone {
|
||||
private final VTimezone component;
|
||||
private final Map<Observance, List<DateValue>> observanceDateCache;
|
||||
final List<Observance> sortedObservances;
|
||||
private final int rawOffset;
|
||||
private final TimeZone utc = TimeZone.getTimeZone("UTC");
|
||||
private final Calendar utcCalendar = Calendar.getInstance(utc);
|
||||
|
||||
/**
|
||||
* Creates a new timezone based on an iCalendar VTIMEZONE component.
|
||||
* @param component the VTIMEZONE component to wrap
|
||||
*/
|
||||
public ICalTimeZone(VTimezone component) {
|
||||
this.component = component;
|
||||
|
||||
int numObservances = component.getStandardTimes().size() + component.getDaylightSavingsTime().size();
|
||||
observanceDateCache = new IdentityHashMap<Observance, List<DateValue>>(numObservances);
|
||||
|
||||
sortedObservances = calculateSortedObservances();
|
||||
|
||||
rawOffset = calculateRawOffset();
|
||||
|
||||
String id = getValue(component.getTimezoneId());
|
||||
if (id != null) {
|
||||
setID(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a list of all the observances in the VTIMEZONE component, sorted
|
||||
* by DTSTART.
|
||||
* @return the sorted observances
|
||||
*/
|
||||
private List<Observance> calculateSortedObservances() {
|
||||
List<DaylightSavingsTime> daylights = component.getDaylightSavingsTime();
|
||||
List<StandardTime> standards = component.getStandardTimes();
|
||||
|
||||
int numObservances = standards.size() + daylights.size();
|
||||
List<Observance> sortedObservances = new ArrayList<Observance>(numObservances);
|
||||
|
||||
sortedObservances.addAll(standards);
|
||||
sortedObservances.addAll(daylights);
|
||||
|
||||
Collections.sort(sortedObservances, new Comparator<Observance>() {
|
||||
public int compare(Observance left, Observance right) {
|
||||
ICalDate startLeft = getValue(left.getDateStart());
|
||||
ICalDate startRight = getValue(right.getDateStart());
|
||||
if (startLeft == null && startRight == null) {
|
||||
return 0;
|
||||
}
|
||||
if (startLeft == null) {
|
||||
return -1;
|
||||
}
|
||||
if (startRight == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return startLeft.getRawComponents().compareTo(startRight.getRawComponents());
|
||||
}
|
||||
});
|
||||
|
||||
return Collections.unmodifiableList(sortedObservances);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName(boolean daylight, int style, Locale locale) {
|
||||
ListIterator<Observance> it = sortedObservances.listIterator(sortedObservances.size());
|
||||
while (it.hasPrevious()) {
|
||||
Observance observance = it.previous();
|
||||
|
||||
if (daylight && observance instanceof DaylightSavingsTime) {
|
||||
List<TimezoneName> names = observance.getTimezoneNames();
|
||||
if (!names.isEmpty()) {
|
||||
String name = names.get(0).getValue();
|
||||
if (name != null) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!daylight && observance instanceof StandardTime) {
|
||||
List<TimezoneName> names = observance.getTimezoneNames();
|
||||
if (!names.isEmpty()) {
|
||||
String name = names.get(0).getValue();
|
||||
if (name != null) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.getDisplayName(daylight, style, locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffset(int era, int year, int month, int day, int dayOfWeek, int millis) {
|
||||
int hour = millis / 1000 / 60 / 60;
|
||||
millis -= hour * 1000 * 60 * 60;
|
||||
int minute = millis / 1000 / 60;
|
||||
millis -= minute * 1000 * 60;
|
||||
int second = millis / 1000;
|
||||
|
||||
Observance observance = getObservance(year, month + 1, day, hour, minute, second);
|
||||
if (observance == null) {
|
||||
/*
|
||||
* Find the first observance that has a DTSTART property and a
|
||||
* TZOFFSETFROM property.
|
||||
*/
|
||||
for (Observance obs : sortedObservances) {
|
||||
ICalDate dateStart = getValue(obs.getDateStart());
|
||||
if (dateStart == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UtcOffset offsetFrom = getValue(obs.getTimezoneOffsetFrom());
|
||||
if (offsetFrom == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return (int) offsetFrom.getMillis();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
UtcOffset offsetTo = getValue(observance.getTimezoneOffsetTo());
|
||||
return (offsetTo == null) ? 0 : (int) offsetTo.getMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawOffset() {
|
||||
return rawOffset;
|
||||
}
|
||||
|
||||
private int calculateRawOffset() {
|
||||
Observance observance = getObservance(new Date());
|
||||
if (observance == null) {
|
||||
//return the offset of the first STANDARD component
|
||||
for (Observance obs : sortedObservances) {
|
||||
if (!(obs instanceof StandardTime)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UtcOffset offsetTo = getValue(obs.getTimezoneOffsetTo());
|
||||
if (offsetTo == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return (int) offsetTo.getMillis();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
UtcOffset offset = getValue((observance instanceof StandardTime) ? observance.getTimezoneOffsetTo() : observance.getTimezoneOffsetFrom());
|
||||
return (offset == null) ? 0 : (int) offset.getMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inDaylightTime(Date date) {
|
||||
if (!useDaylightTime()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Observance observance = getObservance(date);
|
||||
return (observance == null) ? false : (observance instanceof DaylightSavingsTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is not supported by this class.
|
||||
* @throws UnsupportedOperationException thrown when this method is called
|
||||
*/
|
||||
@Override
|
||||
public void setRawOffset(int offset) {
|
||||
throw new UnsupportedOperationException(Messages.INSTANCE.getExceptionMessage(12));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useDaylightTime() {
|
||||
for (Observance observance : sortedObservances) {
|
||||
if (observance instanceof DaylightSavingsTime) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timezone information of a date.
|
||||
* @param date the date
|
||||
* @return the timezone information
|
||||
*/
|
||||
public Boundary getObservanceBoundary(Date date) {
|
||||
utcCalendar.setTime(date);
|
||||
int year = utcCalendar.get(Calendar.YEAR);
|
||||
int month = utcCalendar.get(Calendar.MONTH) + 1;
|
||||
int day = utcCalendar.get(Calendar.DATE);
|
||||
int hour = utcCalendar.get(Calendar.HOUR);
|
||||
int minute = utcCalendar.get(Calendar.MINUTE);
|
||||
int second = utcCalendar.get(Calendar.SECOND);
|
||||
|
||||
return getObservanceBoundary(year, month, day, hour, minute, second);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the observance that a date is effected by.
|
||||
* @param date the date
|
||||
* @return the observance or null if an observance cannot be found
|
||||
*/
|
||||
public Observance getObservance(Date date) {
|
||||
Boundary boundary = getObservanceBoundary(date);
|
||||
return (boundary == null) ? null : boundary.getObservanceIn();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gets the VTIMEZONE component that is being wrapped.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that the ICalTimeZone class makes heavy use of caching. Any
|
||||
* modifications made to the VTIMEZONE component that is returned by this
|
||||
* method may effect the accuracy of this ICalTimeZone instance.
|
||||
* </p>
|
||||
* @return the VTIMEZONE component
|
||||
*/
|
||||
public VTimezone getComponent() {
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the observance that a date is effected by.
|
||||
* @param year the year
|
||||
* @param month the month (1-12)
|
||||
* @param day the day of the month
|
||||
* @param hour the hour
|
||||
* @param minute the minute
|
||||
* @param second the second
|
||||
* @return the observance or null if an observance cannot be found
|
||||
*/
|
||||
private Observance getObservance(int year, int month, int day, int hour, int minute, int second) {
|
||||
Boundary boundary = getObservanceBoundary(year, month, day, hour, minute, second);
|
||||
return (boundary == null) ? null : boundary.getObservanceIn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the observance information of a date.
|
||||
* @param year the year
|
||||
* @param month the month (1-12)
|
||||
* @param day the day of the month
|
||||
* @param hour the hour
|
||||
* @param minute the minute
|
||||
* @param second the second
|
||||
* @return the observance information or null if none was found
|
||||
*/
|
||||
private Boundary getObservanceBoundary(int year, int month, int day, int hour, int minute, int second) {
|
||||
if (sortedObservances.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DateValue givenTime = new DateTimeValueImpl(year, month, day, hour, minute, second);
|
||||
int closestIndex = -1;
|
||||
Observance closest = null;
|
||||
DateValue closestValue = null;
|
||||
for (int i = 0; i < sortedObservances.size(); i++) {
|
||||
Observance observance = sortedObservances.get(i);
|
||||
|
||||
//skip observances that start after the given time
|
||||
ICalDate dtstart = getValue(observance.getDateStart());
|
||||
if (dtstart != null) {
|
||||
DateValue dtstartValue = convertFromRawComponents(dtstart);
|
||||
if (dtstartValue.compareTo(givenTime) > 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
DateValue dateValue = getObservanceDateClosestToTheGivenDate(observance, givenTime, false);
|
||||
if (dateValue != null && (closestValue == null || closestValue.compareTo(dateValue) < 0)) {
|
||||
closestValue = dateValue;
|
||||
closest = observance;
|
||||
closestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
Observance observanceIn = closest;
|
||||
DateValue observanceInStart = closestValue;
|
||||
Observance observanceAfter = null;
|
||||
DateValue observanceAfterStart = null;
|
||||
if (closestIndex < sortedObservances.size() - 1) {
|
||||
observanceAfter = sortedObservances.get(closestIndex + 1);
|
||||
observanceAfterStart = getObservanceDateClosestToTheGivenDate(observanceAfter, givenTime, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* If any of the DTSTART properties are missing their time components,
|
||||
* then observanceInStart/observanceAfterStart could be a DateValue
|
||||
* object. If so, convert it to a DateTimeValue object (see Issue 77).
|
||||
*/
|
||||
if (observanceInStart != null && !(observanceInStart instanceof DateTimeValue)) {
|
||||
observanceInStart = new DTBuilder(observanceInStart).toDateTime();
|
||||
}
|
||||
if (observanceAfterStart != null && !(observanceAfterStart instanceof DateTimeValue)) {
|
||||
observanceAfterStart = new DTBuilder(observanceAfterStart).toDateTime();
|
||||
}
|
||||
|
||||
return new Boundary((DateTimeValue) observanceInStart, observanceIn, (DateTimeValue) observanceAfterStart, observanceAfter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through each of the timezone boundary dates defined by the given
|
||||
* observance and finds the date that comes closest to the given date.
|
||||
* @param observance the observance
|
||||
* @param givenDate the given date
|
||||
* @param after true to return the closest date <b>greater than</b> the
|
||||
* given date, false to return the closest date <b>less than or equal to</b>
|
||||
* the given date.
|
||||
* @return the closest date
|
||||
*/
|
||||
private DateValue getObservanceDateClosestToTheGivenDate(Observance observance, DateValue givenDate, boolean after) {
|
||||
List<DateValue> dateCache = observanceDateCache.get(observance);
|
||||
if (dateCache == null) {
|
||||
dateCache = new ArrayList<DateValue>();
|
||||
observanceDateCache.put(observance, dateCache);
|
||||
}
|
||||
|
||||
if (dateCache.isEmpty()) {
|
||||
DateValue prev = null, cur = null;
|
||||
boolean stopped = false;
|
||||
RecurrenceIterator it = createIterator(observance);
|
||||
while (it.hasNext()) {
|
||||
cur = it.next();
|
||||
dateCache.add(cur);
|
||||
|
||||
if (givenDate.compareTo(cur) < 0) {
|
||||
//stop if we have passed the givenTime
|
||||
stopped = true;
|
||||
break;
|
||||
}
|
||||
|
||||
prev = cur;
|
||||
}
|
||||
return after ? (stopped ? cur : null) : prev;
|
||||
}
|
||||
|
||||
DateValue last = dateCache.get(dateCache.size() - 1);
|
||||
int comparison = last.compareTo(givenDate);
|
||||
if ((after && comparison <= 0) || comparison < 0) {
|
||||
RecurrenceIterator it = createIterator(observance);
|
||||
|
||||
/*
|
||||
* Calling "it.advanceTo()" here causes problems.
|
||||
*
|
||||
* See: https://github.com/mangstadt/biweekly/issues/126
|
||||
*/
|
||||
//it.advanceTo(last);
|
||||
|
||||
DateValue prev = null, cur = null;
|
||||
boolean stopped = false;
|
||||
while (it.hasNext()) {
|
||||
cur = it.next();
|
||||
int curCompareToLast = cur.compareTo(last);
|
||||
if (curCompareToLast < 0) {
|
||||
continue;
|
||||
}
|
||||
if (curCompareToLast > 0) {
|
||||
dateCache.add(cur);
|
||||
}
|
||||
if (curCompareToLast == 0) {
|
||||
//do nothing; don't add to dateCache
|
||||
}
|
||||
|
||||
if (givenDate.compareTo(cur) < 0) {
|
||||
//stop if we have passed the givenTime
|
||||
stopped = true;
|
||||
break;
|
||||
}
|
||||
|
||||
prev = cur;
|
||||
}
|
||||
return after ? (stopped ? cur : null) : prev;
|
||||
}
|
||||
|
||||
/*
|
||||
* The date is somewhere in the cached list, so find it.
|
||||
*
|
||||
* Note: Read the "binarySearch" method Javadoc carefully for an
|
||||
* explanation of its return value.
|
||||
*/
|
||||
int index = Collections.binarySearch(dateCache, givenDate);
|
||||
|
||||
if (index < 0) {
|
||||
/*
|
||||
* The index where the date would be if it was inside the list.
|
||||
*/
|
||||
index = (index * -1) - 1;
|
||||
|
||||
if (after) {
|
||||
/*
|
||||
* This is where the date would be if it was inside the list, so
|
||||
* we want to return the date value that's currently at that
|
||||
* position.
|
||||
*/
|
||||
int afterIndex = index;
|
||||
|
||||
return (afterIndex < dateCache.size()) ? dateCache.get(afterIndex) : null;
|
||||
}
|
||||
|
||||
int beforeIndex = index - 1;
|
||||
if (beforeIndex < 0) {
|
||||
return null;
|
||||
}
|
||||
if (beforeIndex >= dateCache.size()) {
|
||||
return dateCache.get(dateCache.size() - 1);
|
||||
}
|
||||
return dateCache.get(beforeIndex);
|
||||
}
|
||||
|
||||
/*
|
||||
* An exact match was found.
|
||||
*/
|
||||
if (after) {
|
||||
int afterIndex = index + 1; //remember: the date must be >
|
||||
return (afterIndex < dateCache.size()) ? dateCache.get(afterIndex) : null;
|
||||
}
|
||||
return dateCache.get(index); //remember: the date must be <=
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an iterator which iterates over each of the dates in an
|
||||
* observance.
|
||||
* @param observance the observance
|
||||
* @return the iterator
|
||||
*/
|
||||
RecurrenceIterator createIterator(Observance observance) {
|
||||
List<RecurrenceIterator> inclusions = new ArrayList<RecurrenceIterator>();
|
||||
List<RecurrenceIterator> exclusions = new ArrayList<RecurrenceIterator>();
|
||||
|
||||
ICalDate dtstart = getValue(observance.getDateStart());
|
||||
if (dtstart != null) {
|
||||
DateValue dtstartValue = convertFromRawComponents(dtstart);
|
||||
|
||||
//add DTSTART property
|
||||
inclusions.add(new DateValueRecurrenceIterator(Collections.singletonList(dtstartValue)));
|
||||
|
||||
//add RRULE properties
|
||||
for (RecurrenceRule rrule : observance.getProperties(RecurrenceRule.class)) {
|
||||
Recurrence recur = rrule.getValue();
|
||||
if (recur != null) {
|
||||
inclusions.add(RecurrenceIteratorFactory.createRecurrenceIterator(recur, dtstartValue, utc));
|
||||
}
|
||||
}
|
||||
|
||||
//add EXRULE properties
|
||||
for (ExceptionRule exrule : observance.getProperties(ExceptionRule.class)) {
|
||||
Recurrence recur = exrule.getValue();
|
||||
if (recur != null) {
|
||||
exclusions.add(RecurrenceIteratorFactory.createRecurrenceIterator(recur, dtstartValue, utc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//add RDATE properties
|
||||
List<ICalDate> rdates = new ArrayList<ICalDate>();
|
||||
for (RecurrenceDates rdate : observance.getRecurrenceDates()) {
|
||||
rdates.addAll(rdate.getDates());
|
||||
}
|
||||
Collections.sort(rdates);
|
||||
inclusions.add(new DateRecurrenceIterator(rdates));
|
||||
|
||||
//add EXDATE properties
|
||||
List<ICalDate> exdates = new ArrayList<ICalDate>();
|
||||
for (ExceptionDates exdate : observance.getProperties(ExceptionDates.class)) {
|
||||
exdates.addAll(exdate.getValues());
|
||||
}
|
||||
Collections.sort(exdates);
|
||||
exclusions.add(new DateRecurrenceIterator(exdates));
|
||||
|
||||
RecurrenceIterator included = join(inclusions);
|
||||
if (exclusions.isEmpty()) {
|
||||
return included;
|
||||
}
|
||||
|
||||
RecurrenceIterator excluded = join(exclusions);
|
||||
return RecurrenceIteratorFactory.except(included, excluded);
|
||||
}
|
||||
|
||||
private static RecurrenceIterator join(List<RecurrenceIterator> iterators) {
|
||||
if (iterators.isEmpty()) {
|
||||
return new EmptyRecurrenceIterator();
|
||||
}
|
||||
|
||||
RecurrenceIterator first = iterators.get(0);
|
||||
if (iterators.size() == 1) {
|
||||
return first;
|
||||
}
|
||||
|
||||
List<RecurrenceIterator> theRest = iterators.subList(1, iterators.size());
|
||||
return RecurrenceIteratorFactory.join(first, theRest.toArray(new RecurrenceIterator[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* A recurrence iterator that doesn't have any elements.
|
||||
*/
|
||||
private static class EmptyRecurrenceIterator implements RecurrenceIterator {
|
||||
public boolean hasNext() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public DateValue next() {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
public void advanceTo(DateValue newStartUtc) {
|
||||
//empty
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
//RecurrenceIterator does not support this method
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A recurrence iterator that takes a collection of {@link DateValue}
|
||||
* objects.
|
||||
*/
|
||||
private static class DateValueRecurrenceIterator extends IteratorWrapper<DateValue> {
|
||||
public DateValueRecurrenceIterator(Collection<DateValue> dates) {
|
||||
super(dates.iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DateValue toDateValue(DateValue value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A recurrence iterator that takes a collection of {@link ICalDate}
|
||||
* objects.
|
||||
*/
|
||||
private static class DateRecurrenceIterator extends IteratorWrapper<ICalDate> {
|
||||
public DateRecurrenceIterator(Collection<ICalDate> dates) {
|
||||
super(dates.iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DateValue toDateValue(ICalDate value) {
|
||||
return convertFromRawComponents(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A recurrence iterator that wraps an {@link Iterator}.
|
||||
*/
|
||||
private static abstract class IteratorWrapper<T> implements RecurrenceIterator {
|
||||
protected final Iterator<T> it;
|
||||
private DateValue next;
|
||||
|
||||
public IteratorWrapper(Iterator<T> it) {
|
||||
this.it = it;
|
||||
}
|
||||
|
||||
public DateValue next() {
|
||||
if (next != null) {
|
||||
DateValue value = next;
|
||||
next = null;
|
||||
return value;
|
||||
}
|
||||
return toDateValue(it.next());
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return next != null || it.hasNext();
|
||||
}
|
||||
|
||||
public void advanceTo(DateValue newStartUtc) {
|
||||
if (this.next != null && this.next.compareTo(newStartUtc) >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (it.hasNext()) {
|
||||
DateValue next = toDateValue(it.next());
|
||||
if (next.compareTo(newStartUtc) >= 0) {
|
||||
this.next = next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
//RecurrenceIterator does not support this method
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected abstract DateValue toDateValue(T next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds the timezone observance information of a particular date.
|
||||
*/
|
||||
public static class Boundary {
|
||||
private final DateTimeValue observanceInStart, observanceAfterStart;
|
||||
private final Observance observanceIn, observanceAfter;
|
||||
|
||||
public Boundary(DateTimeValue observanceInStart, Observance observanceIn, DateTimeValue observanceAfterStart, Observance observanceAfter) {
|
||||
this.observanceInStart = observanceInStart;
|
||||
this.observanceAfterStart = observanceAfterStart;
|
||||
this.observanceIn = observanceIn;
|
||||
this.observanceAfter = observanceAfter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets start time of the observance that the date resides in.
|
||||
* @return the time
|
||||
*/
|
||||
public DateTimeValue getObservanceInStart() {
|
||||
return observanceInStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the start time the observance that comes after the observance
|
||||
* that the date resides in.
|
||||
* @return the time
|
||||
*/
|
||||
public DateTimeValue getObservanceAfterStart() {
|
||||
return observanceAfterStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the observance that the date resides in.
|
||||
* @return the observance
|
||||
*/
|
||||
public Observance getObservanceIn() {
|
||||
return observanceIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the observance that comes after the observance that the date
|
||||
* resides in.
|
||||
* @return the observance
|
||||
*/
|
||||
public Observance getObservanceAfter() {
|
||||
return observanceAfter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Boundary [observanceInStart=" + observanceInStart + ", observanceAfterStart=" + observanceAfterStart + ", observanceIn=" + observanceIn + ", observanceAfter=" + observanceAfter + "]";
|
||||
}
|
||||
}
|
||||
}
|
@ -1,257 +0,0 @@
|
||||
package biweekly.io;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.parameter.ICalParameters;
|
||||
import biweekly.property.ICalProperty;
|
||||
import biweekly.util.ICalDate;
|
||||
import biweekly.util.ListMultimap;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Stores information used during the parsing of an iCalendar object.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ParseContext {
|
||||
private ICalVersion version;
|
||||
private List<ParseWarning> warnings = new ArrayList<ParseWarning>();
|
||||
private ListMultimap<String, TimezonedDate> timezonedDates = new ListMultimap<String, TimezonedDate>();
|
||||
private List<TimezonedDate> floatingDates = new ArrayList<TimezonedDate>();
|
||||
private Integer lineNumber;
|
||||
private String propertyName;
|
||||
|
||||
/**
|
||||
* Gets the version of the iCalendar object being parsed.
|
||||
* @return the iCalendar version
|
||||
*/
|
||||
public ICalVersion getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the version of the iCalendar object being parsed.
|
||||
* @param version the iCalendar version
|
||||
*/
|
||||
public void setVersion(ICalVersion version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line number the parser is currently on.
|
||||
* @return the line number or null if not applicable
|
||||
*/
|
||||
public Integer getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the line number the parser is currently on.
|
||||
* @param lineNumber the line number or null if not applicable
|
||||
*/
|
||||
public void setLineNumber(Integer lineNumber) {
|
||||
this.lineNumber = lineNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the property that the parser is currently parsing.
|
||||
* @return the property name (e.g. "DTSTART") or null if not applicable
|
||||
*/
|
||||
public String getPropertyName() {
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the property that the parser is currently parsing.
|
||||
* @param propertyName the property name (e.g. "DTSTART") or null if not
|
||||
* applicable
|
||||
*/
|
||||
public void setPropertyName(String propertyName) {
|
||||
this.propertyName = propertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a parsed date to this parse context so its timezone can be applied
|
||||
* to it after the iCalendar object has been parsed (if it has one).
|
||||
* @param icalDate the parsed date
|
||||
* @param property the property that the date value belongs to
|
||||
* @param parameters the property's parameters
|
||||
*/
|
||||
public void addDate(ICalDate icalDate, ICalProperty property, ICalParameters parameters) {
|
||||
if (!icalDate.hasTime()) {
|
||||
//dates don't have timezones
|
||||
return;
|
||||
}
|
||||
|
||||
if (icalDate.getRawComponents().isUtc()) {
|
||||
//it's a UTC date, so it was already parsed under the correct timezone
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO handle UTC offsets within the date strings (not part of iCal standard)
|
||||
String tzid = parameters.getTimezoneId();
|
||||
if (tzid == null) {
|
||||
addFloatingDate(property, icalDate);
|
||||
} else {
|
||||
addTimezonedDate(tzid, property, icalDate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps track of a date-time property value that uses a timezone so it can
|
||||
* be parsed later. Timezones cannot be handled until the entire iCalendar
|
||||
* object has been parsed.
|
||||
* @param tzid the timezone ID (TZID parameter)
|
||||
* @param property the property
|
||||
* @param date the date object that was assigned to the property object
|
||||
*/
|
||||
public void addTimezonedDate(String tzid, ICalProperty property, ICalDate date) {
|
||||
timezonedDates.put(tzid, new TimezonedDate(date, property));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of date-time property values that use a timezone.
|
||||
* @return the date-time property values that use a timezone (key = TZID;
|
||||
* value = the property)
|
||||
*/
|
||||
public ListMultimap<String, TimezonedDate> getTimezonedDates() {
|
||||
return timezonedDates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps track of a date-time property that does not have a timezone
|
||||
* (floating time), so it can be added to the {@link TimezoneInfo} object
|
||||
* after the iCalendar object is parsed.
|
||||
* @param property the property
|
||||
* @param date the property's date value
|
||||
*/
|
||||
public void addFloatingDate(ICalProperty property, ICalDate date) {
|
||||
floatingDates.add(new TimezonedDate(date, property));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date-time properties that are in floating time (lacking a
|
||||
* timezone).
|
||||
* @return the floating date-time properties
|
||||
*/
|
||||
public List<TimezonedDate> getFloatingDates() {
|
||||
return floatingDates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a parse warning.
|
||||
* @param code the warning code
|
||||
* @param args the warning message arguments
|
||||
*/
|
||||
public void addWarning(int code, Object... args) {
|
||||
//@formatter:off
|
||||
warnings.add(new ParseWarning.Builder(this)
|
||||
.message(code, args)
|
||||
.build());
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a parse warning.
|
||||
* @param message the warning message
|
||||
*/
|
||||
public void addWarning(String message) {
|
||||
//@formatter:off
|
||||
warnings.add(new ParseWarning.Builder(this)
|
||||
.message(message)
|
||||
.build());
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parse warnings.
|
||||
* @return the parse warnings
|
||||
*/
|
||||
public List<ParseWarning> getWarnings() {
|
||||
return warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a property whose date-time value has a timezone.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public static class TimezonedDate {
|
||||
private final ICalDate date;
|
||||
private final ICalProperty property;
|
||||
|
||||
/**
|
||||
* @param date the date object that was assigned to the property object
|
||||
* @param property the property object
|
||||
*/
|
||||
public TimezonedDate(ICalDate date, ICalProperty property) {
|
||||
this.date = date;
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date object that was assigned to the property object (should
|
||||
* be parsed under the JVM's default timezone)
|
||||
* @return the date object
|
||||
*/
|
||||
public ICalDate getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the property object.
|
||||
* @return the property
|
||||
*/
|
||||
public ICalProperty getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((date == null) ? 0 : date.hashCode());
|
||||
result = prime * result + ((property == null) ? 0 : property.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
TimezonedDate other = (TimezonedDate) obj;
|
||||
if (date == null) {
|
||||
if (other.date != null) return false;
|
||||
} else if (!date.equals(other.date)) return false;
|
||||
if (property == null) {
|
||||
if (other.property != null) return false;
|
||||
} else if (!property.equals(other.property)) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
package biweekly.io;
|
||||
|
||||
import biweekly.Messages;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a warning that occurred during the parsing of an iCalendar object.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ParseWarning {
|
||||
private final Integer code, lineNumber;
|
||||
private final String propertyName, message;
|
||||
|
||||
private ParseWarning(Integer lineNumber, String propertyName, Integer code, String message) {
|
||||
this.lineNumber = lineNumber;
|
||||
this.propertyName = propertyName;
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the warning code.
|
||||
* @return the warning code or null if no code was specified
|
||||
*/
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line number the warning occurred on.
|
||||
* @return the line number or null if not applicable
|
||||
*/
|
||||
public Integer getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the warning message
|
||||
* @return the warning message
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the property that the warning occurred on.
|
||||
* @return the property name (e.g. "DTSTART") or null if not applicable
|
||||
*/
|
||||
public String getPropertyName() {
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String message = this.message;
|
||||
if (code != null) {
|
||||
message = "(" + code + ") " + message;
|
||||
}
|
||||
|
||||
if (lineNumber == null && propertyName == null) {
|
||||
return message;
|
||||
}
|
||||
|
||||
String key = null;
|
||||
if (lineNumber != null && propertyName == null) {
|
||||
key = "parse.line";
|
||||
} else if (lineNumber == null && propertyName != null) {
|
||||
key = "parse.prop";
|
||||
} else if (lineNumber != null && propertyName != null) {
|
||||
key = "parse.lineWithProp";
|
||||
}
|
||||
|
||||
return Messages.INSTANCE.getMessage(key, lineNumber, propertyName, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs instances of the {@link ParseWarning} class.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public static class Builder {
|
||||
private Integer lineNumber, code;
|
||||
private String propertyName, message;
|
||||
|
||||
/**
|
||||
* Creates an empty builder.
|
||||
*/
|
||||
public Builder() {
|
||||
//empty
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the builder with data from the parse context.
|
||||
* @param context the parse context
|
||||
*/
|
||||
public Builder(ParseContext context) {
|
||||
lineNumber(context.getLineNumber());
|
||||
propertyName(context.getPropertyName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the property that the warning occurred on.
|
||||
* @param propertyName the property name (e.g. "DTSTART") or null if not
|
||||
* applicable
|
||||
* @return this
|
||||
*/
|
||||
public Builder propertyName(String propertyName) {
|
||||
this.propertyName = propertyName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the line number that the warning occurred on.
|
||||
* @param lineNumber the line number or null if not applicable
|
||||
* @return this
|
||||
*/
|
||||
public Builder lineNumber(Integer lineNumber) {
|
||||
this.lineNumber = lineNumber;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the warning message.
|
||||
* @param code the message code
|
||||
* @param args the message arguments
|
||||
* @return this
|
||||
*/
|
||||
public Builder message(int code, Object... args) {
|
||||
this.code = code;
|
||||
message = Messages.INSTANCE.getParseMessage(code, args);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the warning message.
|
||||
* @param message the warning message
|
||||
* @return this
|
||||
*/
|
||||
public Builder message(String message) {
|
||||
code = null;
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the warning message, based on the contents of a
|
||||
* {@link CannotParseException}.
|
||||
* @param exception the exception
|
||||
* @return this
|
||||
*/
|
||||
public Builder message(CannotParseException exception) {
|
||||
return message(exception.getCode(), exception.getArgs());
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the {@link ParseWarning} object.
|
||||
* @return the {@link ParseWarning} object
|
||||
*/
|
||||
public ParseWarning build() {
|
||||
return new ParseWarning(lineNumber, propertyName, code, message);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package biweekly.io;
|
||||
|
||||
import biweekly.ICalendar;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Thrown during the reading or writing of an iCalendar property to show that
|
||||
* the property should not be written to the iCalendar data stream or not be
|
||||
* included in the parsed {@link ICalendar} object.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class SkipMeException extends RuntimeException {
|
||||
private static final long serialVersionUID = 3384029056232963767L;
|
||||
private final String reason;
|
||||
|
||||
/**
|
||||
* Creates a new "skip me" exception.
|
||||
* @param reason the reason why the property was skipped
|
||||
*/
|
||||
public SkipMeException(String reason) {
|
||||
super(reason);
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the reason why the property was skipped.
|
||||
* @return the reason
|
||||
*/
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
}
|
@ -1,398 +0,0 @@
|
||||
package biweekly.io;
|
||||
|
||||
import static biweekly.io.DataModelConverter.convert;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import biweekly.ICalendar;
|
||||
import biweekly.component.ICalComponent;
|
||||
import biweekly.component.VTimezone;
|
||||
import biweekly.io.ParseContext.TimezonedDate;
|
||||
import biweekly.io.scribe.ScribeIndex;
|
||||
import biweekly.io.scribe.component.ICalComponentScribe;
|
||||
import biweekly.io.scribe.property.ICalPropertyScribe;
|
||||
import biweekly.property.Daylight;
|
||||
import biweekly.property.ICalProperty;
|
||||
import biweekly.property.Timezone;
|
||||
import biweekly.property.ValuedProperty;
|
||||
import biweekly.util.ICalDate;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parses iCalendar objects from a data stream.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public abstract class StreamReader implements Closeable {
|
||||
protected final List<ParseWarning> warnings = new ArrayList<ParseWarning>();
|
||||
protected ScribeIndex index = new ScribeIndex();
|
||||
protected ParseContext context;
|
||||
private TimeZone defaultTimezone = TimeZone.getDefault();
|
||||
private GlobalTimezoneIdResolver globalTimezoneIdResolver = new DefaultGlobalTimezoneIdResolver();
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Registers an experimental property scribe. Can also be used to override
|
||||
* the scribe of a standard property (such as DTSTART). Calling this method
|
||||
* is the same as calling:
|
||||
* </p>
|
||||
* <p>
|
||||
* {@code getScribeIndex().register(scribe)}.
|
||||
* </p>
|
||||
* @param scribe the scribe to register
|
||||
*/
|
||||
public void registerScribe(ICalPropertyScribe<? extends ICalProperty> scribe) {
|
||||
index.register(scribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Registers an experimental component scribe. Can also be used to override
|
||||
* the scribe of a standard component (such as VEVENT). Calling this method
|
||||
* is the same as calling:
|
||||
* </p>
|
||||
* <p>
|
||||
* {@code getScribeIndex().register(scribe)}.
|
||||
* </p>
|
||||
* @param scribe the scribe to register
|
||||
*/
|
||||
public void registerScribe(ICalComponentScribe<? extends ICalComponent> scribe) {
|
||||
index.register(scribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object that manages the component/property scribes.
|
||||
* @return the scribe index
|
||||
*/
|
||||
public ScribeIndex getScribeIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the object that manages the component/property scribes.
|
||||
* @param index the scribe index
|
||||
*/
|
||||
public void setScribeIndex(ScribeIndex index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the warnings from the last iCalendar object that was read.
|
||||
* @return the warnings or empty list if there were no warnings
|
||||
*/
|
||||
public List<ParseWarning> getWarnings() {
|
||||
return new ArrayList<ParseWarning>(warnings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timezone that will be used for parsing date property values that
|
||||
* are floating or that have invalid timezone definitions assigned to them.
|
||||
* Defaults to {@link TimeZone#getDefault}.
|
||||
* @return the default timezone
|
||||
*/
|
||||
public TimeZone getDefaultTimezone() {
|
||||
return defaultTimezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timezone that will be used for parsing date property values that
|
||||
* are floating or that have invalid timezone definitions assigned to them.
|
||||
* Defaults to {@link TimeZone#getDefault}.
|
||||
* @param defaultTimezone the default timezone
|
||||
*/
|
||||
public void setDefaultTimezone(TimeZone defaultTimezone) {
|
||||
this.defaultTimezone = defaultTimezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the resolver that maps global timezone IDs to Java {@link TimeZone}
|
||||
* objects. Defaults to {@link DefaultGlobalTimezoneIdResolver}.
|
||||
* @return the resolver
|
||||
*/
|
||||
public GlobalTimezoneIdResolver getGlobalTimezoneIdResolver() {
|
||||
return globalTimezoneIdResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the resolver that maps global timezone IDs to Java {@link TimeZone}
|
||||
* objects. Defaults to {@link DefaultGlobalTimezoneIdResolver}.
|
||||
* @param globalTimezoneIdResolver the resolver
|
||||
*/
|
||||
public void setGlobalTimezoneIdResolver(GlobalTimezoneIdResolver globalTimezoneIdResolver) {
|
||||
this.globalTimezoneIdResolver = globalTimezoneIdResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all iCalendar objects from the data stream.
|
||||
* @return the iCalendar objects
|
||||
* @throws IOException if there's a problem reading from the stream
|
||||
*/
|
||||
public List<ICalendar> readAll() throws IOException {
|
||||
List<ICalendar> icals = new ArrayList<ICalendar>();
|
||||
ICalendar ical;
|
||||
while ((ical = readNext()) != null) {
|
||||
icals.add(ical);
|
||||
}
|
||||
return icals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next iCalendar object from the data stream.
|
||||
* @return the next iCalendar object or null if there are no more
|
||||
* @throws IOException if there's a problem reading from the stream
|
||||
*/
|
||||
public ICalendar readNext() throws IOException {
|
||||
warnings.clear();
|
||||
context = new ParseContext();
|
||||
ICalendar ical = _readNext();
|
||||
if (ical == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ical.setVersion(context.getVersion());
|
||||
handleTimezones(ical);
|
||||
return ical;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next iCalendar object from the data stream.
|
||||
* @return the next iCalendar object or null if there are no more
|
||||
* @throws IOException if there's a problem reading from the stream
|
||||
*/
|
||||
protected abstract ICalendar _readNext() throws IOException;
|
||||
|
||||
private void handleTimezones(ICalendar ical) {
|
||||
TimezoneInfo tzinfo = ical.getTimezoneInfo();
|
||||
|
||||
//convert vCalendar DAYLIGHT and TZ properties to a VTIMEZONE component
|
||||
TimezoneAssignment vcalTimezone = extractVCalTimezone(ical);
|
||||
|
||||
//assign a TimeZone object to each VTIMEZONE component.
|
||||
Iterator<VTimezone> it = ical.getComponents(VTimezone.class).iterator();
|
||||
while (it.hasNext()) {
|
||||
VTimezone component = it.next();
|
||||
|
||||
//make sure the component has an ID
|
||||
String id = ValuedProperty.getValue(component.getTimezoneId());
|
||||
if (id == null || id.trim().isEmpty()) {
|
||||
//note: do not remove invalid VTIMEZONE components from the ICalendar object
|
||||
warnings.add(new ParseWarning.Builder().message(39).build());
|
||||
continue;
|
||||
}
|
||||
|
||||
TimeZone timezone = new ICalTimeZone(component);
|
||||
tzinfo.getTimezones().add(new TimezoneAssignment(timezone, component));
|
||||
|
||||
//remove the component from the ICalendar object
|
||||
it.remove();
|
||||
}
|
||||
|
||||
boolean userChangedTheDefaultTimezone = !defaultTimezone.equals(TimeZone.getDefault());
|
||||
|
||||
if (vcalTimezone != null) {
|
||||
//vCal: parse floating dates according to the DAYLIGHT and TZ properties (which were converted to a VTIMEZONE component)
|
||||
Calendar cal = Calendar.getInstance(vcalTimezone.getTimeZone());
|
||||
for (TimezonedDate timezonedDate : context.getFloatingDates()) {
|
||||
reparseDateUnderDifferentTimezone(timezonedDate, cal);
|
||||
}
|
||||
} else {
|
||||
//iCal: treat floating dates as floating dates
|
||||
for (TimezonedDate timezonedDate : context.getFloatingDates()) {
|
||||
tzinfo.setFloating(timezonedDate.getProperty(), true);
|
||||
}
|
||||
|
||||
//convert all floating dates to the default timezone
|
||||
if (userChangedTheDefaultTimezone) {
|
||||
Calendar cal = Calendar.getInstance(defaultTimezone);
|
||||
for (TimezonedDate timezonedDate : context.getFloatingDates()) {
|
||||
reparseDateUnderDifferentTimezone(timezonedDate, cal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//convert all date values to their appropriate timezone
|
||||
for (Map.Entry<String, List<TimezonedDate>> entry : context.getTimezonedDates()) {
|
||||
String tzid = entry.getKey();
|
||||
|
||||
//determine which timezone is associated with the given TZID
|
||||
TimezoneAssignment assignment = determineTimezoneAssignment(tzid, tzinfo);
|
||||
|
||||
/*
|
||||
* If a timezone assignment could not be found for the given TZID
|
||||
* and the user did not change the default timezone, then there is
|
||||
* no need to further process the properties that are assigned to
|
||||
* this TZID--the date value should remain unchanged (parsed under
|
||||
* the local machine's default timezone), and its TZID parameter
|
||||
* should also remain.
|
||||
*/
|
||||
if (assignment == null && !userChangedTheDefaultTimezone) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//convert each property to the timezone
|
||||
TimeZone tz = (assignment == null) ? defaultTimezone : assignment.getTimeZone();
|
||||
Calendar cal = Calendar.getInstance(tz);
|
||||
for (TimezonedDate timezonedDate : entry.getValue()) {
|
||||
ICalProperty property = timezonedDate.getProperty();
|
||||
|
||||
if (assignment != null) {
|
||||
tzinfo.setTimezone(property, assignment);
|
||||
|
||||
/*
|
||||
* Only remove the TZID parameter if the TZID is *valid*.
|
||||
* Invalid TZID parameters should remain so that user can
|
||||
* inspect the invalid information.
|
||||
*/
|
||||
property.getParameters().setTimezoneId(null);
|
||||
}
|
||||
|
||||
reparseDateUnderDifferentTimezone(timezonedDate, cal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void reparseDateUnderDifferentTimezone(TimezonedDate timezonedDate, Calendar cal) {
|
||||
ICalDate date = timezonedDate.getDate();
|
||||
|
||||
//parse its raw date components under its real timezone
|
||||
Date realDate = date.getRawComponents().toDate(cal);
|
||||
|
||||
//update the Date object with the new timestamp
|
||||
date.setTime(realDate.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the timezone definition that is associated with the given ID.
|
||||
* @param tzid the timezone ID
|
||||
* @param tzinfo the timezone settings of the iCalendar object
|
||||
* @return the timezone definition or null to use the default timezone
|
||||
*/
|
||||
private TimezoneAssignment determineTimezoneAssignment(String tzid, TimezoneInfo tzinfo) {
|
||||
boolean isOlsenId = tzid.startsWith("/");
|
||||
|
||||
//HANDLE OLSEN IDS======================================================
|
||||
|
||||
if (isOlsenId) {
|
||||
String globalId = tzid.substring(1);
|
||||
TimeZone timezone = globalTimezoneIdResolver.resolve(globalId);
|
||||
if (timezone != null) {
|
||||
/*
|
||||
* Olsen ID is valid. Everything is Ok.
|
||||
*/
|
||||
TimezoneAssignment assignment = new TimezoneAssignment(timezone, globalId);
|
||||
tzinfo.getTimezones().add(assignment);
|
||||
return assignment;
|
||||
}
|
||||
|
||||
/*
|
||||
* Even though the TZID is marked as an Olsen ID, and the timezone
|
||||
* isn't recognized by Java, try looking for a VTIMEZONE component
|
||||
* that matches it.
|
||||
*
|
||||
* This is done as a courtesy and is not required by the specs.
|
||||
*/
|
||||
TimezoneAssignment assignment = tzinfo.getTimezoneById(tzid);
|
||||
int warning;
|
||||
if (assignment == null) {
|
||||
/*
|
||||
* TZID does not match any VTIMEZONE components, use the default
|
||||
* timezone.
|
||||
*/
|
||||
warning = 38;
|
||||
} else {
|
||||
warning = 43;
|
||||
}
|
||||
|
||||
warnings.add(new ParseWarning.Builder().message(warning, tzid).build());
|
||||
return assignment;
|
||||
}
|
||||
|
||||
//HANDLE VTIMEZONE COMPONENT IDS========================================
|
||||
|
||||
TimezoneAssignment assignment = tzinfo.getTimezoneById(tzid);
|
||||
if (assignment != null) {
|
||||
/*
|
||||
* VTIMEZONE component with the given TZID was found.
|
||||
* Everything is Ok.
|
||||
*/
|
||||
return assignment;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try treating the TZID as an Olsen timezone ID, even though it does
|
||||
* not start with a forward slash.
|
||||
*
|
||||
* This is done as a courtesy for users who do not know they must prefix
|
||||
* Olsen IDs with a forward slash. It is not required by the specs.
|
||||
*/
|
||||
String globalId = tzid;
|
||||
TimeZone timezone = globalTimezoneIdResolver.resolve(globalId);
|
||||
int warning;
|
||||
if (timezone == null) {
|
||||
/*
|
||||
* TZID is not a valid Olsen ID, use the default timezone.
|
||||
*/
|
||||
warning = 38;
|
||||
assignment = null;
|
||||
} else {
|
||||
/*
|
||||
* TZID was successfully parsed as an Olsen ID.
|
||||
*/
|
||||
warning = 37;
|
||||
assignment = new TimezoneAssignment(timezone, globalId);
|
||||
tzinfo.getTimezones().add(assignment);
|
||||
}
|
||||
|
||||
warnings.add(new ParseWarning.Builder().message(warning, globalId).build());
|
||||
return assignment;
|
||||
}
|
||||
|
||||
private TimezoneAssignment extractVCalTimezone(ICalendar ical) {
|
||||
List<Daylight> daylights = ical.removeProperties(Daylight.class);
|
||||
List<Timezone> timezones = ical.removeProperties(Timezone.class);
|
||||
|
||||
Timezone timezone = timezones.isEmpty() ? null : timezones.get(0);
|
||||
VTimezone vcalComponent = convert(daylights, timezone);
|
||||
if (vcalComponent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TimeZone icalTimezone = new ICalTimeZone(vcalComponent);
|
||||
TimezoneInfo tzinfo = ical.getTimezoneInfo();
|
||||
TimezoneAssignment assignment = new TimezoneAssignment(icalTimezone, vcalComponent);
|
||||
tzinfo.setDefaultTimezone(assignment);
|
||||
|
||||
return assignment;
|
||||
}
|
||||
}
|
@ -1,216 +0,0 @@
|
||||
package biweekly.io;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.ICalendar;
|
||||
import biweekly.Messages;
|
||||
import biweekly.component.ICalComponent;
|
||||
import biweekly.component.RawComponent;
|
||||
import biweekly.component.VTimezone;
|
||||
import biweekly.io.scribe.ScribeIndex;
|
||||
import biweekly.io.scribe.component.ICalComponentScribe;
|
||||
import biweekly.io.scribe.property.ICalPropertyScribe;
|
||||
import biweekly.property.ICalProperty;
|
||||
import biweekly.property.RawProperty;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Writes iCalendar objects to a data stream.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public abstract class StreamWriter implements Closeable {
|
||||
protected ScribeIndex index = new ScribeIndex();
|
||||
protected WriteContext context;
|
||||
protected TimezoneAssignment globalTimezone;
|
||||
private TimezoneInfo tzinfo;
|
||||
|
||||
/**
|
||||
* Writes an iCalendar object to the data stream.
|
||||
* @param ical the iCalendar object to write
|
||||
* @throws IllegalArgumentException if the scribe class for a component or
|
||||
* property object cannot be found (only happens when an experimental
|
||||
* property/component scribe is not registered with the
|
||||
* {@code registerScribe} method.)
|
||||
* @throws IOException if there's a problem writing to the data stream
|
||||
*/
|
||||
public void write(ICalendar ical) throws IOException {
|
||||
Collection<Class<?>> unregistered = findScribeless(ical);
|
||||
if (!unregistered.isEmpty()) {
|
||||
List<String> classNames = new ArrayList<String>(unregistered.size());
|
||||
for (Class<?> clazz : unregistered) {
|
||||
classNames.add(clazz.getName());
|
||||
}
|
||||
throw Messages.INSTANCE.getIllegalArgumentException(13, classNames);
|
||||
}
|
||||
|
||||
tzinfo = ical.getTimezoneInfo();
|
||||
context = new WriteContext(getTargetVersion(), tzinfo, globalTimezone);
|
||||
_write(ical);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link VTimezone} components that need to be written to the
|
||||
* output stream.
|
||||
* @return the components
|
||||
*/
|
||||
protected Collection<VTimezone> getTimezoneComponents() {
|
||||
if (globalTimezone != null) {
|
||||
VTimezone component = globalTimezone.getComponent();
|
||||
return (component == null) ? Collections.<VTimezone> emptyList() : Collections.singletonList(component);
|
||||
}
|
||||
|
||||
return tzinfo.getComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the version that the next iCalendar object will be written as.
|
||||
* @return the version
|
||||
*/
|
||||
protected abstract ICalVersion getTargetVersion();
|
||||
|
||||
/**
|
||||
* Writes an iCalendar object to the data stream.
|
||||
* @param ical the iCalendar object to write
|
||||
* @throws IOException if there's a problem writing to the data stream
|
||||
*/
|
||||
protected abstract void _write(ICalendar ical) throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the timezone that all date/time property values will be formatted
|
||||
* in. If set, this setting will override the timezone information
|
||||
* associated with each {@link ICalendar} object.
|
||||
* @return the global timezone or null if not set (defaults to null)
|
||||
*/
|
||||
public TimezoneAssignment getGlobalTimezone() {
|
||||
return globalTimezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timezone that all date/time property values will be formatted
|
||||
* in. This is a convenience method that overrides the timezone information
|
||||
* associated with each {@link ICalendar} object that is passed into this
|
||||
* writer.
|
||||
* @param globalTimezone the global timezone or null not to set a global
|
||||
* timezone (defaults to null)
|
||||
*/
|
||||
public void setGlobalTimezone(TimezoneAssignment globalTimezone) {
|
||||
this.globalTimezone = globalTimezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Registers an experimental property scribe. Can also be used to override
|
||||
* the scribe of a standard property (such as DTSTART). Calling this method
|
||||
* is the same as calling:
|
||||
* </p>
|
||||
* <p>
|
||||
* {@code getScribeIndex().register(scribe)}.
|
||||
* </p>
|
||||
* @param scribe the scribe to register
|
||||
*/
|
||||
public void registerScribe(ICalPropertyScribe<? extends ICalProperty> scribe) {
|
||||
index.register(scribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Registers an experimental component scribe. Can also be used to override
|
||||
* the scribe of a standard component (such as VEVENT). Calling this method
|
||||
* is the same as calling:
|
||||
* </p>
|
||||
* <p>
|
||||
* {@code getScribeIndex().register(scribe)}.
|
||||
* </p>
|
||||
* @param scribe the scribe to register
|
||||
*/
|
||||
public void registerScribe(ICalComponentScribe<? extends ICalComponent> scribe) {
|
||||
index.register(scribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object that manages the component/property scribes.
|
||||
* @return the scribe index
|
||||
*/
|
||||
public ScribeIndex getScribeIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the object that manages the component/property scribes.
|
||||
* @param scribe the scribe index
|
||||
*/
|
||||
public void setScribeIndex(ScribeIndex scribe) {
|
||||
this.index = scribe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the component/property classes that don't have scribes associated
|
||||
* with them.
|
||||
* @param ical the iCalendar object
|
||||
* @return the component/property classes
|
||||
*/
|
||||
private Collection<Class<?>> findScribeless(ICalendar ical) {
|
||||
Set<Class<?>> unregistered = new HashSet<Class<?>>();
|
||||
LinkedList<ICalComponent> components = new LinkedList<ICalComponent>();
|
||||
components.add(ical);
|
||||
|
||||
while (!components.isEmpty()) {
|
||||
ICalComponent component = components.removeLast();
|
||||
|
||||
Class<? extends ICalComponent> componentClass = component.getClass();
|
||||
if (componentClass != RawComponent.class && index.getComponentScribe(componentClass) == null) {
|
||||
unregistered.add(componentClass);
|
||||
}
|
||||
|
||||
for (Map.Entry<Class<? extends ICalProperty>, List<ICalProperty>> entry : component.getProperties()) {
|
||||
List<ICalProperty> properties = entry.getValue();
|
||||
if (properties.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Class<? extends ICalProperty> clazz = entry.getKey();
|
||||
if (clazz != RawProperty.class && index.getPropertyScribe(clazz) == null) {
|
||||
unregistered.add(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
components.addAll(component.getComponents().values());
|
||||
}
|
||||
|
||||
return unregistered;
|
||||
}
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
package biweekly.io;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
import biweekly.Messages;
|
||||
import biweekly.component.VTimezone;
|
||||
import biweekly.property.TimezoneId;
|
||||
import biweekly.property.ValuedProperty;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a timezone definition that is used within an iCalendar object.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class TimezoneAssignment {
|
||||
private final TimeZone timezone;
|
||||
private final VTimezone component;
|
||||
private final String globalId;
|
||||
|
||||
/**
|
||||
* Creates a timezone that will be inserted into the iCalendar object as a
|
||||
* VTIMEZONE component. This what most iCalendar files use.
|
||||
* @param timezone the Java timezone object
|
||||
* @param component the iCalendar component
|
||||
* @throws IllegalArgumentException if the given {@link VTimezone} component
|
||||
* doesn't have a {@link TimezoneId} property
|
||||
*/
|
||||
public TimezoneAssignment(TimeZone timezone, VTimezone component) {
|
||||
String id = ValuedProperty.getValue(component.getTimezoneId());
|
||||
if (id == null || id.trim().isEmpty()) {
|
||||
throw Messages.INSTANCE.getIllegalArgumentException(14);
|
||||
}
|
||||
|
||||
this.timezone = timezone;
|
||||
this.component = component;
|
||||
this.globalId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Creates a timezone that will be inserted into the iCalendar object as a
|
||||
* "global ID". This means that a {@link VTimezone} component containing the
|
||||
* timezone definition will NOT be inserted into the iCalendar object.
|
||||
* </p>
|
||||
* <p>
|
||||
* Because the timezone definition is not included inside of the iCalendar
|
||||
* object, the client consuming the iCalendar object must know how to
|
||||
* interpret such an ID. The iCalendar specification does not specify a list
|
||||
* of such IDs, but suggests using the naming convention of an existing
|
||||
* timezone specification, such as the
|
||||
* <a href="http://www.twinsun.com/tz/tz-link.htm">public-domain TZ
|
||||
* database</a>.
|
||||
* </p>
|
||||
* @param timezone the Java timezone object
|
||||
* @param globalId the global ID (e.g. "America/New_York")
|
||||
*/
|
||||
public TimezoneAssignment(TimeZone timezone, String globalId) {
|
||||
this.timezone = timezone;
|
||||
this.component = null;
|
||||
this.globalId = globalId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a timezone whose VTIMEZONE component is downloaded from
|
||||
* <a href="http://www.tzurl.org">tzurl.org</a>.
|
||||
* @param timezone the Java timezone object
|
||||
* @param outlookCompatible true to download a {@link VTimezone} component
|
||||
* that is tailored for Microsoft Outlook email clients, false to download a
|
||||
* standards-based one.
|
||||
* @return the timezone assignment
|
||||
* @throws IllegalArgumentException if an appropriate VTIMEZONE component
|
||||
* cannot be found on the website
|
||||
*/
|
||||
public static TimezoneAssignment download(TimeZone timezone, boolean outlookCompatible) {
|
||||
TzUrlDotOrgGenerator generator = new TzUrlDotOrgGenerator(outlookCompatible);
|
||||
VTimezone component = generator.generate(timezone);
|
||||
return new TimezoneAssignment(timezone, component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Java object associated with the timezone.
|
||||
* @return the Java object
|
||||
*/
|
||||
public TimeZone getTimeZone() {
|
||||
return timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the iCalendar component associated with the timezone.
|
||||
* @return the component or null if the timezone uses a global ID
|
||||
*/
|
||||
public VTimezone getComponent() {
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the global ID associated with the timezone.
|
||||
* @return the global ID or null if the timezone uses an iCalendar component
|
||||
*/
|
||||
public String getGlobalId() {
|
||||
return globalId;
|
||||
}
|
||||
}
|
@ -1,305 +0,0 @@
|
||||
package biweekly.io;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import biweekly.component.VTimezone;
|
||||
import biweekly.property.ICalProperty;
|
||||
import biweekly.property.TimezoneId;
|
||||
import biweekly.property.ValuedProperty;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Holds the timezone-related settings of an iCalendar object.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class TimezoneInfo {
|
||||
@SuppressWarnings("serial")
|
||||
private final Collection<TimezoneAssignment> assignments = new HashSet<TimezoneAssignment>() {
|
||||
@Override
|
||||
public boolean remove(Object assignment) {
|
||||
//remove all property assignments
|
||||
Collection<TimezoneAssignment> values = propertyTimezones.values();
|
||||
while (values.remove(assignment)) {
|
||||
//empty
|
||||
}
|
||||
|
||||
return super.remove(assignment);
|
||||
}
|
||||
};
|
||||
private final Map<ICalProperty, TimezoneAssignment> propertyTimezones = new IdentityHashMap<ICalProperty, TimezoneAssignment>();
|
||||
private final List<ICalProperty> floatingProperties = new ArrayList<ICalProperty>();
|
||||
|
||||
private TimezoneAssignment defaultTimezone;
|
||||
private boolean globalFloatingTime = false;
|
||||
|
||||
/**
|
||||
* Gets all the timezones assigned to this object.
|
||||
* @return the timezones (collection is mutable)
|
||||
*/
|
||||
public Collection<TimezoneAssignment> getTimezones() {
|
||||
return assignments;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gets the timezone to format all date/time values in (by default, all
|
||||
* dates are formatted in UTC).
|
||||
* </p>
|
||||
* <p>
|
||||
* The default timezone is not used for properties that are configured to
|
||||
* use their own timezone (see {@link #setTimezone}).
|
||||
* </p>
|
||||
* @return the timezone or null if using UTC
|
||||
*/
|
||||
public TimezoneAssignment getDefaultTimezone() {
|
||||
return defaultTimezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets the timezone to format all date/time values in (by default, all
|
||||
* dates are formatted in UTC).
|
||||
* </p>
|
||||
* <p>
|
||||
* The default timezone is not used for properties that are configured to
|
||||
* use their own timezone (see {@link #setTimezone}).
|
||||
* </p>
|
||||
* @param timezone the timezone or null to use UTC
|
||||
*/
|
||||
public void setDefaultTimezone(TimezoneAssignment timezone) {
|
||||
if (timezone == null) {
|
||||
if (defaultTimezone != null && !propertyTimezones.containsValue(defaultTimezone)) {
|
||||
assignments.remove(defaultTimezone);
|
||||
}
|
||||
} else {
|
||||
assignments.add(timezone);
|
||||
}
|
||||
|
||||
defaultTimezone = timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a timezone to a specific property.
|
||||
* @param property the property
|
||||
* @param timezone the timezone or null to format the property according to
|
||||
* the default timezone (see {@link #setDefaultTimezone}).
|
||||
*/
|
||||
public void setTimezone(ICalProperty property, TimezoneAssignment timezone) {
|
||||
if (timezone == null) {
|
||||
TimezoneAssignment existing = propertyTimezones.remove(property);
|
||||
if (existing != null && existing != defaultTimezone && !propertyTimezones.containsValue(existing)) {
|
||||
assignments.remove(existing);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
assignments.add(timezone);
|
||||
propertyTimezones.put(property, timezone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timezone that is assigned to a property.
|
||||
* @param property the property
|
||||
* @return the timezone or null if no timezone is assigned to the property
|
||||
*/
|
||||
public TimezoneAssignment getTimezone(ICalProperty property) {
|
||||
return propertyTimezones.get(property);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Determines the timezone that a particular property should be formatted in
|
||||
* when written to an output stream.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note: You should call {@link #isFloating} first, to determine if the
|
||||
* property's value is floating (without a timezone).
|
||||
* </p>
|
||||
* @param property the property
|
||||
* @return the timezone or null for UTC
|
||||
*/
|
||||
public TimezoneAssignment getTimezoneToWriteIn(ICalProperty property) {
|
||||
TimezoneAssignment assignment = getTimezone(property);
|
||||
return (assignment == null) ? defaultTimezone : assignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timezone whose {@link VTimezone} component contains a
|
||||
* {@link TimezoneId} property with the given value.
|
||||
* @param tzid the value of the {@link TimezoneId} property
|
||||
* @return the timezone or null if not found
|
||||
*/
|
||||
public TimezoneAssignment getTimezoneById(String tzid) {
|
||||
for (TimezoneAssignment assignment : assignments) {
|
||||
VTimezone component = assignment.getComponent();
|
||||
if (component == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String componentId = ValuedProperty.getValue(component.getTimezoneId());
|
||||
if (tzid.equals(componentId)) {
|
||||
return assignment;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gets whether to format all date/time values as floating times (defaults
|
||||
* to false).
|
||||
* </p>
|
||||
* <p>
|
||||
* This setting does not apply to properties whose floating time settings
|
||||
* are configured individually (see: {@link #setFloating}) or that are
|
||||
* configured to use their own timezone (see {@link #setTimezone}).
|
||||
* </p>
|
||||
* <p>
|
||||
* A floating time value does not have a timezone associated with it, and is
|
||||
* to be interpreted as being in the local timezone of the computer that is
|
||||
* consuming the iCalendar object.
|
||||
* </p>
|
||||
* @return true if enabled, false if disabled
|
||||
*/
|
||||
public boolean isGlobalFloatingTime() {
|
||||
return globalFloatingTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets whether to format all date/time values as floating times (defaults
|
||||
* to false).
|
||||
* </p>
|
||||
* <p>
|
||||
* This setting does not apply to properties whose floating time settings
|
||||
* are configured individually (see: {@link #setFloating}) or that are
|
||||
* configured to use their own timezone (see {@link #setTimezone}).
|
||||
* </p>
|
||||
* <p>
|
||||
* A floating time value does not have a timezone associated with it, and is
|
||||
* to be interpreted as being in the local timezone of the computer that is
|
||||
* consuming the iCalendar object.
|
||||
* </p>
|
||||
* @param enable true to enable, false to disable
|
||||
*/
|
||||
public void setGlobalFloatingTime(boolean enable) {
|
||||
globalFloatingTime = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a property value should be formatted in floating time when
|
||||
* written to an output stream.
|
||||
* @param property the property
|
||||
* @return true to format in floating time, false not to
|
||||
*/
|
||||
public boolean isFloating(ICalProperty property) {
|
||||
if (containsIdentity(floatingProperties, property)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (propertyTimezones.containsKey(property)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return globalFloatingTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets whether a property value should be formatted in floating time when
|
||||
* written to an output stream (by default, floating time is disabled for
|
||||
* all properties).
|
||||
* </p>
|
||||
* <p>
|
||||
* A floating time value does not have a timezone associated with it, and is
|
||||
* to be interpreted as being in the local timezone of the computer that is
|
||||
* consuming the iCalendar object.
|
||||
* </p>
|
||||
* @param property the property
|
||||
* @param enable true to enable floating time for this property, false to
|
||||
* disable
|
||||
*/
|
||||
public void setFloating(ICalProperty property, boolean enable) {
|
||||
if (enable) {
|
||||
floatingProperties.add(property);
|
||||
} else {
|
||||
removeIdentity(floatingProperties, property);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all of the iCalendar {@link VTimezone} components that have been
|
||||
* registered with this object.
|
||||
* @return the components (this collection is immutable)
|
||||
*/
|
||||
public Collection<VTimezone> getComponents() {
|
||||
List<VTimezone> components = new ArrayList<VTimezone>(assignments.size());
|
||||
for (TimezoneAssignment assignment : assignments) {
|
||||
VTimezone component = assignment.getComponent();
|
||||
if (component != null) {
|
||||
components.add(component);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(components);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an object from a list using reference equality.
|
||||
* @param list the list
|
||||
* @param object the object to remove
|
||||
*/
|
||||
private static <T> void removeIdentity(List<T> list, T object) {
|
||||
Iterator<T> it = list.iterator();
|
||||
while (it.hasNext()) {
|
||||
if (object == it.next()) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for an item in a list using reference equality.
|
||||
* @param list the list
|
||||
* @param object the object to search for
|
||||
* @return true if the object was found, false if not
|
||||
*/
|
||||
private static <T> boolean containsIdentity(List<T> list, T object) {
|
||||
for (T item : list) {
|
||||
if (item == object) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
package biweekly.io;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import biweekly.ICalendar;
|
||||
import biweekly.component.VTimezone;
|
||||
import biweekly.io.text.ICalReader;
|
||||
import biweekly.property.TimezoneId;
|
||||
import biweekly.property.ValuedProperty;
|
||||
import biweekly.util.IOUtils;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Downloads {@link VTimezone} components from <a
|
||||
* href="http://www.tzurl.org">tzurl.org</a>. This class is thread-safe.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class TzUrlDotOrgGenerator {
|
||||
private static final Map<URI, VTimezone> cache = Collections.synchronizedMap(new HashMap<URI, VTimezone>());
|
||||
private final String baseUrl;
|
||||
|
||||
/**
|
||||
* Creates a new tzurl.org generator.
|
||||
* @param outlookCompatible true to download {@link VTimezone} components
|
||||
* that are tailored for Microsoft Outlook email clients, false to download
|
||||
* standards-based ones
|
||||
*/
|
||||
public TzUrlDotOrgGenerator(boolean outlookCompatible) {
|
||||
baseUrl = "http://www.tzurl.org/zoneinfo" + (outlookCompatible ? "-outlook" : "") + "/";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an iCalendar {@link VTimezone} components from a Java
|
||||
* {@link TimeZone} object.
|
||||
* @param timezone the timezone object
|
||||
* @return the timezone component
|
||||
* @throws IllegalArgumentException if a timezone definition cannot be found
|
||||
*/
|
||||
public VTimezone generate(TimeZone timezone) throws IllegalArgumentException {
|
||||
URI uri;
|
||||
try {
|
||||
uri = new URI(buildUrl(timezone));
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
|
||||
VTimezone component = cache.get(uri);
|
||||
if (component != null) {
|
||||
return component.copy();
|
||||
}
|
||||
|
||||
ICalendar ical;
|
||||
ICalReader reader = null;
|
||||
try {
|
||||
reader = new ICalReader(getInputStream(uri));
|
||||
ical = reader.readNext();
|
||||
} catch (FileNotFoundException e) {
|
||||
throw notFound(e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(reader);
|
||||
}
|
||||
|
||||
/*
|
||||
* There should always be exactly one iCalendar object in the file, but
|
||||
* check to be sure.
|
||||
*/
|
||||
if (ical == null) {
|
||||
throw notFound(null);
|
||||
}
|
||||
|
||||
/*
|
||||
* There should always be exactly one VTIMEZONE component, but check to
|
||||
* be sure.
|
||||
*/
|
||||
TimezoneInfo tzinfo = ical.getTimezoneInfo();
|
||||
Collection<VTimezone> components = tzinfo.getComponents();
|
||||
if (components.isEmpty()) {
|
||||
components = ical.getComponents(VTimezone.class); //VTIMEZONE components without TZID properties are treated as ordinary components
|
||||
if (components.isEmpty()) {
|
||||
throw notFound(null);
|
||||
}
|
||||
}
|
||||
|
||||
component = components.iterator().next();
|
||||
|
||||
/*
|
||||
* There should always be a TZID property, but just in case there there
|
||||
* isn't one, create one.
|
||||
*/
|
||||
TimezoneId id = component.getTimezoneId();
|
||||
if (id == null) {
|
||||
component.setTimezoneId(timezone.getID());
|
||||
} else {
|
||||
String value = ValuedProperty.getValue(id);
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
id.setValue(timezone.getID());
|
||||
}
|
||||
}
|
||||
|
||||
cache.put(uri, component);
|
||||
return component.copy();
|
||||
}
|
||||
|
||||
private String buildUrl(TimeZone timezone) {
|
||||
return baseUrl + timezone.getID();
|
||||
}
|
||||
|
||||
//for unit testing
|
||||
InputStream getInputStream(URI uri) throws IOException {
|
||||
return uri.toURL().openStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the internal cache of downloaded timezone definitions.
|
||||
*/
|
||||
public static void clearCache() {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
private static IllegalArgumentException notFound(Exception e) {
|
||||
return new IllegalArgumentException("Timezone ID not recognized.", e);
|
||||
}
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
package biweekly.io;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.ICalendar;
|
||||
import biweekly.component.ICalComponent;
|
||||
import biweekly.util.ICalDate;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Stores information used to write the properties in an iCalendar object.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class WriteContext {
|
||||
private final ICalVersion version;
|
||||
private final TimezoneInfo timezoneOptions;
|
||||
private final TimezoneAssignment globalTimezone;
|
||||
private final List<Date> dates = new ArrayList<Date>();
|
||||
private ICalComponent parent;
|
||||
|
||||
public WriteContext(ICalVersion version, TimezoneInfo timezoneOptions, TimezoneAssignment globalTimezone) {
|
||||
this.version = version;
|
||||
this.timezoneOptions = timezoneOptions;
|
||||
this.globalTimezone = globalTimezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the version of the iCalendar object that is being written.
|
||||
* @return the iCalendar version
|
||||
*/
|
||||
public ICalVersion getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timezone options for this iCalendar object.
|
||||
* @return the timezone options
|
||||
*/
|
||||
public TimezoneInfo getTimezoneInfo() {
|
||||
return timezoneOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the global timezone to format all date/time values in, regardless of
|
||||
* each individual {@link ICalendar}'s timezone settings.
|
||||
* @return the global timezone or null if not set
|
||||
*/
|
||||
public TimezoneAssignment getGlobalTimezone() {
|
||||
return globalTimezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parent component of the property that is being written.
|
||||
* @return the parent component
|
||||
*/
|
||||
public ICalComponent getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parent component of the property that is being written.
|
||||
* @param parent the parent component
|
||||
*/
|
||||
public void setParent(ICalComponent parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timezoned date-time property values that are in the iCalendar
|
||||
* object.
|
||||
* @return the timezoned date-time property values
|
||||
*/
|
||||
public List<Date> getDates() {
|
||||
return dates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the timezoned date-time values that are being written. This is
|
||||
* used to generate a DAYLIGHT property for vCalendar objects.
|
||||
* @param floating true if the date is floating, false if not
|
||||
* @param tz the timezone to format the date in or null for UTC
|
||||
* @param date the date value
|
||||
*/
|
||||
public void addDate(ICalDate date, boolean floating, TimeZone tz) {
|
||||
if (date != null && date.hasTime() && !floating && tz != null) {
|
||||
dates.add(date);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
package biweekly.io.chain;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
|
||||
import biweekly.Biweekly;
|
||||
import biweekly.io.StreamReader;
|
||||
import biweekly.io.json.JCalReader;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Chainer class for parsing jCals (JSON-encoded iCalendar objects).
|
||||
* @see Biweekly#parseJson(InputStream)
|
||||
* @see Biweekly#parseJson(File)
|
||||
* @see Biweekly#parseJson(Reader)
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ChainingJsonParser<T extends ChainingJsonParser<?>> extends ChainingParser<T> {
|
||||
public ChainingJsonParser(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
public ChainingJsonParser(InputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
public ChainingJsonParser(Reader reader) {
|
||||
super(reader);
|
||||
}
|
||||
|
||||
public ChainingJsonParser(File file) {
|
||||
super(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
StreamReader constructReader() throws IOException {
|
||||
if (string != null) {
|
||||
return new JCalReader(string);
|
||||
}
|
||||
if (in != null) {
|
||||
return new JCalReader(in);
|
||||
}
|
||||
if (reader != null) {
|
||||
return new JCalReader(reader);
|
||||
}
|
||||
return new JCalReader(file);
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
package biweekly.io.chain;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import biweekly.Biweekly;
|
||||
import biweekly.ICalendar;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Chainer class for parsing jCals (JSON-encoded iCalendar objects) from
|
||||
* strings.
|
||||
* @see Biweekly#parseJson(String)
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ChainingJsonStringParser extends ChainingJsonParser<ChainingJsonStringParser> {
|
||||
public ChainingJsonStringParser(String json) {
|
||||
super(json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICalendar first() {
|
||||
try {
|
||||
return super.first();
|
||||
} catch (IOException e) {
|
||||
//should never be thrown because we're reading from a string
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ICalendar> all() {
|
||||
try {
|
||||
return super.all();
|
||||
} catch (IOException e) {
|
||||
//should never be thrown because we're reading from a string
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
package biweekly.io.chain;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Collection;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import biweekly.Biweekly;
|
||||
import biweekly.ICalendar;
|
||||
import biweekly.component.ICalComponent;
|
||||
import biweekly.io.json.JCalWriter;
|
||||
import biweekly.io.scribe.component.ICalComponentScribe;
|
||||
import biweekly.io.scribe.property.ICalPropertyScribe;
|
||||
import biweekly.property.ICalProperty;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Chainer class for writing jCals (JSON-encoded iCalendar objects).
|
||||
* @see Biweekly#writeJson(Collection)
|
||||
* @see Biweekly#writeJson(ICalendar...)
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ChainingJsonWriter extends ChainingWriter<ChainingJsonWriter> {
|
||||
private boolean prettyPrint = false;
|
||||
|
||||
/**
|
||||
* @param icals the iCalendar objects to write
|
||||
*/
|
||||
public ChainingJsonWriter(Collection<ICalendar> icals) {
|
||||
super(icals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not to pretty-print the JSON.
|
||||
* @param prettyPrint true to pretty-print it, false not to (defaults to
|
||||
* false)
|
||||
* @return this
|
||||
*/
|
||||
public ChainingJsonWriter prettyPrint(boolean prettyPrint) {
|
||||
this.prettyPrint = prettyPrint;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChainingJsonWriter tz(TimeZone defaultTimeZone, boolean outlookCompatible) {
|
||||
return super.tz(defaultTimeZone, outlookCompatible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChainingJsonWriter register(ICalPropertyScribe<? extends ICalProperty> scribe) {
|
||||
return super.register(scribe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChainingJsonWriter register(ICalComponentScribe<? extends ICalComponent> scribe) {
|
||||
return super.register(scribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the iCalendar objects to a string.
|
||||
* @return the JSON string
|
||||
*/
|
||||
public String go() {
|
||||
StringWriter sw = new StringWriter();
|
||||
try {
|
||||
go(sw);
|
||||
} catch (IOException e) {
|
||||
//should never be thrown because we're writing to a string
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the iCalendar objects to an output stream.
|
||||
* @param out the output stream to write to
|
||||
* @throws IOException if there's a problem writing to the output stream
|
||||
*/
|
||||
public void go(OutputStream out) throws IOException {
|
||||
go(new JCalWriter(out, wrapInArray()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the iCalendar objects to a file.
|
||||
* @param file the file to write to
|
||||
* @throws IOException if there's a problem writing to the file
|
||||
*/
|
||||
public void go(File file) throws IOException {
|
||||
JCalWriter writer = new JCalWriter(file, wrapInArray());
|
||||
try {
|
||||
go(writer);
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the iCalendar objects to a writer.
|
||||
* @param writer the writer to write to
|
||||
* @throws IOException if there's a problem writing to the writer
|
||||
*/
|
||||
public void go(Writer writer) throws IOException {
|
||||
go(new JCalWriter(writer, wrapInArray()));
|
||||
}
|
||||
|
||||
private void go(JCalWriter writer) throws IOException {
|
||||
if (defaultTimeZone != null) {
|
||||
writer.setGlobalTimezone(defaultTimeZone);
|
||||
}
|
||||
writer.setPrettyPrint(prettyPrint);
|
||||
if (index != null) {
|
||||
writer.setScribeIndex(index);
|
||||
}
|
||||
try {
|
||||
for (ICalendar ical : icals) {
|
||||
writer.write(ical);
|
||||
writer.flush();
|
||||
}
|
||||
} finally {
|
||||
writer.closeJsonStream();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean wrapInArray() {
|
||||
return icals.size() > 1;
|
||||
}
|
||||
}
|
@ -1,206 +0,0 @@
|
||||
package biweekly.io.chain;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import biweekly.ICalendar;
|
||||
import biweekly.component.ICalComponent;
|
||||
import biweekly.io.ParseWarning;
|
||||
import biweekly.io.StreamReader;
|
||||
import biweekly.io.scribe.ScribeIndex;
|
||||
import biweekly.io.scribe.component.ICalComponentScribe;
|
||||
import biweekly.io.scribe.property.ICalPropertyScribe;
|
||||
import biweekly.property.ICalProperty;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parent class for all chaining parsers. This class is package-private in order
|
||||
* to hide it from the generated Javadocs.
|
||||
* @author Michael Angstadt
|
||||
* @param <T> the object instance's type (for method chaining)
|
||||
*/
|
||||
abstract class ChainingParser<T extends ChainingParser<?>> {
|
||||
final String string;
|
||||
final InputStream in;
|
||||
final Reader reader;
|
||||
final File file;
|
||||
|
||||
ScribeIndex index;
|
||||
List<List<ParseWarning>> warnings;
|
||||
TimeZone defaultTimezone;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final T this_ = (T) this;
|
||||
|
||||
ChainingParser(String string) {
|
||||
this(string, null, null, null);
|
||||
}
|
||||
|
||||
ChainingParser(InputStream in) {
|
||||
this(null, in, null, null);
|
||||
}
|
||||
|
||||
ChainingParser(Reader reader) {
|
||||
this(null, null, reader, null);
|
||||
}
|
||||
|
||||
ChainingParser(File file) {
|
||||
this(null, null, null, file);
|
||||
}
|
||||
|
||||
ChainingParser() {
|
||||
this(null, null, null, null);
|
||||
}
|
||||
|
||||
private ChainingParser(String string, InputStream in, Reader reader, File file) {
|
||||
this.string = string;
|
||||
this.in = in;
|
||||
this.reader = reader;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a property scribe.
|
||||
* @param scribe the scribe
|
||||
* @return this
|
||||
*/
|
||||
public T register(ICalPropertyScribe<? extends ICalProperty> scribe) {
|
||||
if (index == null) {
|
||||
index = new ScribeIndex();
|
||||
}
|
||||
index.register(scribe);
|
||||
return this_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a component scribe.
|
||||
* @param scribe the scribe
|
||||
* @return this
|
||||
*/
|
||||
public T register(ICalComponentScribe<? extends ICalComponent> scribe) {
|
||||
if (index == null) {
|
||||
index = new ScribeIndex();
|
||||
}
|
||||
index.register(scribe);
|
||||
return this_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a list object that any parser warnings will be put into.
|
||||
* @param warnings the list object that will be populated with the warnings
|
||||
* of each parsed iCalendar object. Each element in the list is a list of
|
||||
* warnings for one parsed object. Therefore, the size of this list will be
|
||||
* equal to the number of parsed iCalendar objects. If an iCalendar object
|
||||
* does not have any warnings, then its warning list will be empty.
|
||||
* @return this
|
||||
*/
|
||||
public T warnings(List<List<ParseWarning>> warnings) {
|
||||
this.warnings = warnings;
|
||||
return this_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timezone that will be used for parsing date property values that
|
||||
* are floating or that have invalid timezone definitions assigned to them.
|
||||
* Defaults to {@link TimeZone#getDefault}.
|
||||
* @param defaultTimezone the default timezone
|
||||
* @return this
|
||||
*/
|
||||
public T defaultTimezone(TimeZone defaultTimezone) {
|
||||
this.defaultTimezone = defaultTimezone;
|
||||
return this_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the first iCalendar object from the stream.
|
||||
* @return the iCalendar object or null if there are none
|
||||
* @throws IOException if there's an I/O problem
|
||||
*/
|
||||
public ICalendar first() throws IOException {
|
||||
StreamReader reader = constructReader();
|
||||
if (index != null) {
|
||||
reader.setScribeIndex(index);
|
||||
}
|
||||
if (defaultTimezone != null) {
|
||||
reader.setDefaultTimezone(defaultTimezone);
|
||||
}
|
||||
|
||||
try {
|
||||
ICalendar ical = reader.readNext();
|
||||
if (warnings != null) {
|
||||
warnings.add(reader.getWarnings());
|
||||
}
|
||||
return ical;
|
||||
} finally {
|
||||
if (closeWhenDone()) {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all iCalendar objects from the stream.
|
||||
* @return the parsed iCalendar objects
|
||||
* @throws IOException if there's an I/O problem
|
||||
*/
|
||||
public List<ICalendar> all() throws IOException {
|
||||
StreamReader reader = constructReader();
|
||||
if (index != null) {
|
||||
reader.setScribeIndex(index);
|
||||
}
|
||||
if (defaultTimezone != null) {
|
||||
reader.setDefaultTimezone(defaultTimezone);
|
||||
}
|
||||
|
||||
try {
|
||||
List<ICalendar> icals = new ArrayList<ICalendar>();
|
||||
ICalendar ical;
|
||||
while ((ical = reader.readNext()) != null) {
|
||||
if (warnings != null) {
|
||||
warnings.add(reader.getWarnings());
|
||||
}
|
||||
icals.add(ical);
|
||||
}
|
||||
return icals;
|
||||
} finally {
|
||||
if (closeWhenDone()) {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract StreamReader constructReader() throws IOException;
|
||||
|
||||
private boolean closeWhenDone() {
|
||||
return in == null && reader == null;
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
package biweekly.io.chain;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
|
||||
import biweekly.Biweekly;
|
||||
import biweekly.io.StreamReader;
|
||||
import biweekly.io.text.ICalReader;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Chainer class for parsing traditional, plain-text iCalendar objects.
|
||||
* @see Biweekly#parse(InputStream)
|
||||
* @see Biweekly#parse(File)
|
||||
* @see Biweekly#parse(Reader)
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ChainingTextParser<T extends ChainingTextParser<?>> extends ChainingParser<T> {
|
||||
private boolean caretDecoding = true;
|
||||
|
||||
public ChainingTextParser(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
public ChainingTextParser(InputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
public ChainingTextParser(Reader reader) {
|
||||
super(reader);
|
||||
}
|
||||
|
||||
public ChainingTextParser(File file) {
|
||||
super(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the reader will decode characters in parameter values that
|
||||
* use circumflex accent encoding (enabled by default). This only applies to
|
||||
* version 2.0 iCalendar objects.
|
||||
*
|
||||
* @param enable true to use circumflex accent decoding, false not to
|
||||
* @return this
|
||||
* @see ICalReader#setCaretDecodingEnabled(boolean)
|
||||
* @see <a href="http://tools.ietf.org/html/rfc6868">RFC 6868</a>
|
||||
*/
|
||||
public T caretDecoding(boolean enable) {
|
||||
caretDecoding = enable;
|
||||
return this_;
|
||||
}
|
||||
|
||||
@Override
|
||||
StreamReader constructReader() throws IOException {
|
||||
ICalReader reader = newReader();
|
||||
reader.setCaretDecodingEnabled(caretDecoding);
|
||||
return reader;
|
||||
}
|
||||
|
||||
private ICalReader newReader() throws IOException {
|
||||
if (string != null) {
|
||||
return new ICalReader(string);
|
||||
}
|
||||
if (in != null) {
|
||||
return new ICalReader(in);
|
||||
}
|
||||
if (reader != null) {
|
||||
return new ICalReader(reader);
|
||||
}
|
||||
return new ICalReader(file);
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
package biweekly.io.chain;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import biweekly.Biweekly;
|
||||
import biweekly.ICalendar;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Chainer class for parsing traditional, plain-text iCalendar objects from
|
||||
* strings.
|
||||
* @see Biweekly#parse(String)
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ChainingTextStringParser extends ChainingTextParser<ChainingTextStringParser> {
|
||||
public ChainingTextStringParser(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICalendar first() {
|
||||
try {
|
||||
return super.first();
|
||||
} catch (IOException e) {
|
||||
//should never be thrown because we're reading from a string
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ICalendar> all() {
|
||||
try {
|
||||
return super.all();
|
||||
} catch (IOException e) {
|
||||
//should never be thrown because we're reading from a string
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,233 +0,0 @@
|
||||
package biweekly.io.chain;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Collection;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import biweekly.Biweekly;
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.ICalendar;
|
||||
import biweekly.component.ICalComponent;
|
||||
import biweekly.io.scribe.component.ICalComponentScribe;
|
||||
import biweekly.io.scribe.property.ICalPropertyScribe;
|
||||
import biweekly.io.text.ICalWriter;
|
||||
import biweekly.property.ICalProperty;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Chainer class for writing traditional, plain-text iCalendar objects.
|
||||
* @see Biweekly#write(Collection)
|
||||
* @see Biweekly#write(ICalendar...)
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ChainingTextWriter extends ChainingWriter<ChainingTextWriter> {
|
||||
private ICalVersion version;
|
||||
private boolean caretEncoding = false;
|
||||
private boolean foldLines = true;
|
||||
|
||||
/**
|
||||
* @param icals the iCalendar objects to write
|
||||
*/
|
||||
public ChainingTextWriter(Collection<ICalendar> icals) {
|
||||
super(icals);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets the version that all the iCalendar objects will be marshalled to.
|
||||
* The version that is attached to each individual {@link ICalendar} object
|
||||
* will be ignored.
|
||||
* </p>
|
||||
* <p>
|
||||
* If no version is passed into this method, the writer will look at the
|
||||
* version attached to each individual {@link ICalendar} object and marshal
|
||||
* it to that version. And if a {@link ICalendar} object has no version
|
||||
* attached to it, then it will be marshalled to version 2.0.
|
||||
* </p>
|
||||
* @param version the version to marshal the iCalendar objects to
|
||||
* @return this
|
||||
*/
|
||||
public ChainingTextWriter version(ICalVersion version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the writer will use circumflex accent encoding for parameter
|
||||
* values (disabled by default). This only applies to version 2.0 iCalendar
|
||||
* objects.
|
||||
* @param enable true to use circumflex accent encoding, false not to
|
||||
* @return this
|
||||
* @see ICalWriter#setCaretEncodingEnabled(boolean)
|
||||
* @see <a href="http://tools.ietf.org/html/rfc6868">RFC 6868</a>
|
||||
*/
|
||||
public ChainingTextWriter caretEncoding(boolean enable) {
|
||||
this.caretEncoding = enable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets whether to fold long lines. Line folding is when long lines are
|
||||
* split up into multiple lines. No data is lost or changed when a line is
|
||||
* folded.
|
||||
* </p>
|
||||
* <p>
|
||||
* Line folding is enabled by default. If the iCalendar consumer is not
|
||||
* parsing your iCalendar objects properly, disabling line folding may help.
|
||||
* </p>
|
||||
* @param foldLines true to enable line folding, false to disable it
|
||||
* (defaults to true)
|
||||
* @return this
|
||||
*/
|
||||
public ChainingTextWriter foldLines(boolean foldLines) {
|
||||
this.foldLines = foldLines;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChainingTextWriter tz(TimeZone defaultTimeZone, boolean outlookCompatible) {
|
||||
return super.tz(defaultTimeZone, outlookCompatible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChainingTextWriter register(ICalPropertyScribe<? extends ICalProperty> scribe) {
|
||||
return super.register(scribe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChainingTextWriter register(ICalComponentScribe<? extends ICalComponent> scribe) {
|
||||
return super.register(scribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the iCalendar objects to a string.
|
||||
* @return the iCalendar string
|
||||
*/
|
||||
public String go() {
|
||||
StringWriter sw = new StringWriter();
|
||||
try {
|
||||
go(sw);
|
||||
} catch (IOException e) {
|
||||
//should never be thrown because we're writing to a string
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the iCalendar objects to an output stream.
|
||||
* @param out the output stream to write to
|
||||
* @throws IOException if there's a problem writing to the output stream
|
||||
*/
|
||||
public void go(OutputStream out) throws IOException {
|
||||
go(new ICalWriter(out, getICalWriterConstructorVersion()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the iCalendar objects to a file. If the file exists, it will be
|
||||
* overwritten.
|
||||
* @param file the file to write to
|
||||
* @throws IOException if there's a problem writing to the file
|
||||
*/
|
||||
public void go(File file) throws IOException {
|
||||
go(file, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the iCalendar objects to a file.
|
||||
* @param file the file to write to
|
||||
* @param append true to append onto the end of the file, false to overwrite
|
||||
* it
|
||||
* @throws IOException if there's a problem writing to the file
|
||||
*/
|
||||
public void go(File file, boolean append) throws IOException {
|
||||
ICalWriter writer = new ICalWriter(file, append, getICalWriterConstructorVersion());
|
||||
try {
|
||||
go(writer);
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the iCalendar objects to a writer.
|
||||
* @param writer the writer to write to
|
||||
* @throws IOException if there's a problem writing to the writer
|
||||
*/
|
||||
public void go(Writer writer) throws IOException {
|
||||
go(new ICalWriter(writer, getICalWriterConstructorVersion()));
|
||||
}
|
||||
|
||||
private void go(ICalWriter writer) throws IOException {
|
||||
writer.setCaretEncodingEnabled(caretEncoding);
|
||||
if (!foldLines) {
|
||||
writer.getVObjectWriter().getFoldedLineWriter().setLineLength(null);
|
||||
}
|
||||
if (defaultTimeZone != null) {
|
||||
writer.setGlobalTimezone(defaultTimeZone);
|
||||
}
|
||||
if (index != null) {
|
||||
writer.setScribeIndex(index);
|
||||
}
|
||||
|
||||
for (ICalendar ical : icals) {
|
||||
if (version == null) {
|
||||
//use the version that's assigned to each individual iCalendar object
|
||||
ICalVersion icalVersion = ical.getVersion();
|
||||
if (icalVersion == null) {
|
||||
icalVersion = ICalVersion.V2_0;
|
||||
}
|
||||
writer.setTargetVersion(icalVersion);
|
||||
}
|
||||
writer.write(ical);
|
||||
writer.flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gets the {@link ICalVersion} object to pass into the {@link ICalWriter}
|
||||
* constructor. The constructor does not allow a null version, so this
|
||||
* method ensures that a non-null version is passed in.
|
||||
* </p>
|
||||
* <p>
|
||||
* If the user hasn't chosen a version, the version that is passed into the
|
||||
* constructor doesn't matter. This is because the writer's target version
|
||||
* is reset every time an iCalendar object is written (see the
|
||||
* {@link #go(ICalWriter)} method).
|
||||
* </p>
|
||||
* @return the version to pass into the constructor
|
||||
*/
|
||||
private ICalVersion getICalWriterConstructorVersion() {
|
||||
return (version == null) ? ICalVersion.V2_0 : version;
|
||||
}
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
package biweekly.io.chain;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import biweekly.ICalendar;
|
||||
import biweekly.component.ICalComponent;
|
||||
import biweekly.io.TimezoneAssignment;
|
||||
import biweekly.io.scribe.ScribeIndex;
|
||||
import biweekly.io.scribe.component.ICalComponentScribe;
|
||||
import biweekly.io.scribe.property.ICalPropertyScribe;
|
||||
import biweekly.property.ICalProperty;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parent class for all chaining writers. This class is package-private in order
|
||||
* to hide it from the generated Javadocs.
|
||||
* @author Michael Angstadt
|
||||
* @param <T> the object instance's type (for method chaining)
|
||||
*/
|
||||
class ChainingWriter<T extends ChainingWriter<?>> {
|
||||
final Collection<ICalendar> icals;
|
||||
ScribeIndex index;
|
||||
// boolean prodId = true;
|
||||
// boolean versionStrict = true;
|
||||
TimezoneAssignment defaultTimeZone = null;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private final T this_ = (T) this;
|
||||
|
||||
/**
|
||||
* @param icals the iCalendar objects to write
|
||||
*/
|
||||
ChainingWriter(Collection<ICalendar> icals) {
|
||||
this.icals = icals;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets the timezone to use when outputting date values (defaults to UTC).
|
||||
* </p>
|
||||
* <p>
|
||||
* This method downloads an appropriate VTIMEZONE component from the <a
|
||||
* href="http://www.tzurl.org">tzurl.org</a> website.
|
||||
* </p>
|
||||
* @param defaultTimeZone the default timezone or null for UTC
|
||||
* @param outlookCompatible true to download a VTIMEZONE component that is
|
||||
* tailored for Microsoft Outlook email clients, false to download a
|
||||
* standards-based one
|
||||
* @return this
|
||||
* @throws IllegalArgumentException if an appropriate VTIMEZONE component
|
||||
* cannot be found on the website
|
||||
*/
|
||||
T tz(TimeZone defaultTimeZone, boolean outlookCompatible) {
|
||||
this.defaultTimeZone = (defaultTimeZone == null) ? null : TimezoneAssignment.download(defaultTimeZone, outlookCompatible);
|
||||
return this_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a property scribe.
|
||||
* @param scribe the scribe to register
|
||||
* @return this
|
||||
*/
|
||||
T register(ICalPropertyScribe<? extends ICalProperty> scribe) {
|
||||
if (index == null) {
|
||||
index = new ScribeIndex();
|
||||
}
|
||||
index.register(scribe);
|
||||
return this_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a component scribe.
|
||||
* @param scribe the scribe to register
|
||||
* @return this
|
||||
*/
|
||||
T register(ICalComponentScribe<? extends ICalComponent> scribe) {
|
||||
if (index == null) {
|
||||
index = new ScribeIndex();
|
||||
}
|
||||
index.register(scribe);
|
||||
return this_;
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
package biweekly.io.chain;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import biweekly.Biweekly;
|
||||
import biweekly.ICalendar;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Chainer class for parsing xCals (XML-encoded iCalendar objects) from strings
|
||||
* or DOMs.
|
||||
* @see Biweekly#parseXml(String)
|
||||
* @see Biweekly#parseXml(Document)
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ChainingXmlMemoryParser extends ChainingXmlParser<ChainingXmlMemoryParser> {
|
||||
public ChainingXmlMemoryParser(String xml) {
|
||||
super(xml);
|
||||
}
|
||||
|
||||
public ChainingXmlMemoryParser(Document dom) {
|
||||
super(dom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICalendar first() {
|
||||
try {
|
||||
return super.first();
|
||||
} catch (IOException e) {
|
||||
//should never be thrown because we're reading from a string
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ICalendar> all() {
|
||||
try {
|
||||
return super.all();
|
||||
} catch (IOException e) {
|
||||
//should never be thrown because we're reading from a string
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
package biweekly.io.chain;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import biweekly.Biweekly;
|
||||
import biweekly.io.StreamReader;
|
||||
import biweekly.io.xml.XCalReader;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Chainer class for parsing xCals (XML-encoded iCalendar objects).
|
||||
* @see Biweekly#parseXml(InputStream)
|
||||
* @see Biweekly#parseXml(File)
|
||||
* @see Biweekly#parseXml(Reader)
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ChainingXmlParser<T extends ChainingXmlParser<?>> extends ChainingParser<T> {
|
||||
private Document dom;
|
||||
|
||||
public ChainingXmlParser(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
public ChainingXmlParser(InputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
public ChainingXmlParser(File file) {
|
||||
super(file);
|
||||
}
|
||||
|
||||
public ChainingXmlParser(Reader reader) {
|
||||
super(reader);
|
||||
}
|
||||
|
||||
public ChainingXmlParser(Document dom) {
|
||||
this.dom = dom;
|
||||
}
|
||||
|
||||
@Override
|
||||
StreamReader constructReader() throws IOException {
|
||||
if (string != null) {
|
||||
return new XCalReader(string);
|
||||
}
|
||||
if (in != null) {
|
||||
return new XCalReader(in);
|
||||
}
|
||||
if (reader != null) {
|
||||
return new XCalReader(reader);
|
||||
}
|
||||
if (file != null) {
|
||||
return new XCalReader(file);
|
||||
}
|
||||
return new XCalReader(dom);
|
||||
}
|
||||
}
|
@ -1,213 +0,0 @@
|
||||
package biweekly.io.chain;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerException;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import biweekly.Biweekly;
|
||||
import biweekly.ICalDataType;
|
||||
import biweekly.ICalendar;
|
||||
import biweekly.component.ICalComponent;
|
||||
import biweekly.io.scribe.component.ICalComponentScribe;
|
||||
import biweekly.io.scribe.property.ICalPropertyScribe;
|
||||
import biweekly.io.xml.XCalDocument;
|
||||
import biweekly.io.xml.XCalDocument.XCalDocumentStreamWriter;
|
||||
import biweekly.io.xml.XCalOutputProperties;
|
||||
import biweekly.property.ICalProperty;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Chainer class for writing xCal (XML-encoded iCalendar objects).
|
||||
* @see Biweekly#writeXml(Collection)
|
||||
* @see Biweekly#writeXml(ICalendar...)
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ChainingXmlWriter extends ChainingWriter<ChainingXmlWriter> {
|
||||
private final XCalOutputProperties outputProperties = new XCalOutputProperties();
|
||||
private final Map<String, ICalDataType> parameterDataTypes = new HashMap<String, ICalDataType>(0);
|
||||
|
||||
/**
|
||||
* @param icals the iCValendar objects to write
|
||||
*/
|
||||
public ChainingXmlWriter(Collection<ICalendar> icals) {
|
||||
super(icals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of indent spaces to use for pretty-printing. If not set,
|
||||
* then the XML will not be pretty-printed.
|
||||
* @param indent the number of spaces in the indent string or "null" not to
|
||||
* pretty-print (disabled by default)
|
||||
* @return this
|
||||
*/
|
||||
public ChainingXmlWriter indent(Integer indent) {
|
||||
outputProperties.setIndent(indent);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the XML version to use. Note that many JDKs only support 1.0
|
||||
* natively. For XML 1.1 support, add a JAXP library like <a href=
|
||||
* "http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22xalan%22%20AND%20a%3A%22xalan%22"
|
||||
* >xalan</a> to your project.
|
||||
* @param xmlVersion the XML version (defaults to "1.0")
|
||||
* @return this
|
||||
*/
|
||||
public ChainingXmlWriter xmlVersion(String xmlVersion) {
|
||||
outputProperties.setXmlVersion(xmlVersion);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns an output property to the JAXP transformer (see
|
||||
* {@link Transformer#setOutputProperty}).
|
||||
* @param name the property name
|
||||
* @param value the property value
|
||||
* @return this
|
||||
*/
|
||||
public ChainingXmlWriter outputProperty(String name, String value) {
|
||||
outputProperties.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns all of the given output properties to the JAXP transformer (see
|
||||
* {@link Transformer#setOutputProperty}).
|
||||
* @param outputProperties the properties
|
||||
* @return this
|
||||
*/
|
||||
public ChainingXmlWriter outputProperties(Map<String, String> outputProperties) {
|
||||
this.outputProperties.putAll(outputProperties);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChainingXmlWriter tz(TimeZone defaultTimeZone, boolean outlookCompatible) {
|
||||
return super.tz(defaultTimeZone, outlookCompatible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChainingXmlWriter register(ICalPropertyScribe<? extends ICalProperty> scribe) {
|
||||
return super.register(scribe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChainingXmlWriter register(ICalComponentScribe<? extends ICalComponent> scribe) {
|
||||
return super.register(scribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the data type of a non-standard parameter. Non-standard
|
||||
* parameters use the "unknown" data type by default.
|
||||
* @param parameterName the parameter name (e.g. "x-foo")
|
||||
* @param dataType the data type
|
||||
* @return this
|
||||
*/
|
||||
public ChainingXmlWriter register(String parameterName, ICalDataType dataType) {
|
||||
parameterDataTypes.put(parameterName, dataType);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the iCalendar objects to a string.
|
||||
* @return the XML document
|
||||
*/
|
||||
public String go() {
|
||||
return createXCalDocument().write(outputProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the iCalendar objects to an output stream.
|
||||
* @param out the output stream to write to
|
||||
* @throws TransformerException if there's a problem writing to the output
|
||||
* stream
|
||||
*/
|
||||
public void go(OutputStream out) throws TransformerException {
|
||||
createXCalDocument().write(out, outputProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the iCalendar objects to a file.
|
||||
* @param file the file to write to
|
||||
* @throws IOException if the file can't be opened
|
||||
* @throws TransformerException if there's a problem writing to the file
|
||||
*/
|
||||
public void go(File file) throws IOException, TransformerException {
|
||||
createXCalDocument().write(file, outputProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the iCalendar objects to a writer.
|
||||
* @param writer the writer to write to
|
||||
* @throws TransformerException if there's a problem writing to the writer
|
||||
*/
|
||||
public void go(Writer writer) throws TransformerException {
|
||||
createXCalDocument().write(writer, outputProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an XML document object model (DOM) containing the iCalendar
|
||||
* objects.
|
||||
* @return the DOM
|
||||
*/
|
||||
public Document dom() {
|
||||
return createXCalDocument().getDocument();
|
||||
}
|
||||
|
||||
private XCalDocument createXCalDocument() {
|
||||
XCalDocument document = new XCalDocument();
|
||||
|
||||
XCalDocumentStreamWriter writer = document.writer();
|
||||
if (defaultTimeZone != null) {
|
||||
writer.setGlobalTimezone(defaultTimeZone);
|
||||
}
|
||||
for (Map.Entry<String, ICalDataType> entry : parameterDataTypes.entrySet()) {
|
||||
String parameterName = entry.getKey();
|
||||
ICalDataType dataType = entry.getValue();
|
||||
writer.registerParameterDataType(parameterName, dataType);
|
||||
}
|
||||
if (index != null) {
|
||||
writer.setScribeIndex(index);
|
||||
}
|
||||
|
||||
for (ICalendar ical : icals) {
|
||||
writer.write(ical);
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
/**
|
||||
* Contains classes used in the chaining API.
|
||||
* @see biweekly.Biweekly
|
||||
*/
|
||||
package biweekly.io.chain;
|
@ -1,84 +0,0 @@
|
||||
package biweekly.io.json;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import biweekly.ICalendar;
|
||||
import biweekly.io.scribe.ScribeIndex;
|
||||
import biweekly.io.scribe.property.ICalPropertyScribe;
|
||||
import biweekly.property.ICalProperty;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Deserializes jCals within the jackson-databind framework.
|
||||
* @author Buddy Gorven
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class JCalDeserializer extends JsonDeserializer<ICalendar> {
|
||||
private ScribeIndex index = new ScribeIndex();
|
||||
|
||||
@Override
|
||||
public ICalendar deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException {
|
||||
@SuppressWarnings("resource")
|
||||
JCalReader reader = new JCalReader(parser);
|
||||
reader.setScribeIndex(index);
|
||||
return reader.readNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Registers a property scribe. This is the same as calling:
|
||||
* </p>
|
||||
* <p>
|
||||
* {@code getScribeIndex().register(scribe)}
|
||||
* </p>
|
||||
* @param scribe the scribe to register
|
||||
*/
|
||||
public void registerScribe(ICalPropertyScribe<? extends ICalProperty> scribe) {
|
||||
index.register(scribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the scribe index.
|
||||
* @return the scribe index
|
||||
*/
|
||||
public ScribeIndex getScribeIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scribe index.
|
||||
* @param index the scribe index
|
||||
*/
|
||||
public void setScribeIndex(ScribeIndex index) {
|
||||
this.index = index;
|
||||
}
|
||||
}
|
@ -1,145 +0,0 @@
|
||||
package biweekly.io.json;
|
||||
|
||||
import biweekly.Biweekly;
|
||||
import biweekly.ICalendar;
|
||||
import biweekly.io.TimezoneAssignment;
|
||||
import biweekly.io.scribe.ScribeIndex;
|
||||
import biweekly.io.scribe.property.ICalPropertyScribe;
|
||||
import biweekly.property.ICalProperty;
|
||||
|
||||
import com.fasterxml.jackson.core.Version;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Module for jackson-databind that serializes and deserializes jCals.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Example:</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new JCalModule());
|
||||
* ICalendar result = mapper.readValue(..., ICalendar.class);
|
||||
* </pre>
|
||||
* @author Buddy Gorven
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class JCalModule extends SimpleModule {
|
||||
private static final long serialVersionUID = 8022429868572303471L;
|
||||
private static final String MODULE_NAME = "biweekly-jcal";
|
||||
private static final Version MODULE_VERSION = moduleVersion();
|
||||
|
||||
private final JCalDeserializer deserializer = new JCalDeserializer();
|
||||
private final JCalSerializer serializer = new JCalSerializer();
|
||||
|
||||
private ScribeIndex index;
|
||||
|
||||
/**
|
||||
* Creates the module.
|
||||
*/
|
||||
public JCalModule() {
|
||||
super(MODULE_NAME, MODULE_VERSION);
|
||||
|
||||
setScribeIndex(new ScribeIndex());
|
||||
addSerializer(serializer);
|
||||
addDeserializer(ICalendar.class, deserializer);
|
||||
}
|
||||
|
||||
private static Version moduleVersion() {
|
||||
String[] split = Biweekly.VERSION.split("[.-]");
|
||||
if (split.length < 3) {
|
||||
/*
|
||||
* This can happen during development if the "biweekly.properties"
|
||||
* file has not been filtered by Maven.
|
||||
*/
|
||||
return new Version(0, 0, 0, "", Biweekly.GROUP_ID, Biweekly.ARTIFACT_ID);
|
||||
}
|
||||
|
||||
int major = Integer.parseInt(split[0]);
|
||||
int minor = Integer.parseInt(split[1]);
|
||||
int patch = Integer.parseInt(split[2]);
|
||||
String snapshot = (split.length > 3) ? split[3] : "RELEASE";
|
||||
|
||||
return new Version(major, minor, patch, snapshot, Biweekly.GROUP_ID, Biweekly.ARTIFACT_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Registers a property scribe. This is the same as calling:
|
||||
* </p>
|
||||
* <p>
|
||||
* {@code getScribeIndex().register(scribe)}
|
||||
* </p>
|
||||
* @param scribe the scribe to register
|
||||
*/
|
||||
public void registerScribe(ICalPropertyScribe<? extends ICalProperty> scribe) {
|
||||
index.register(scribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the scribe index used by the serializer and deserializer.
|
||||
* @return the scribe index
|
||||
*/
|
||||
public ScribeIndex getScribeIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scribe index for the serializer and deserializer to use.
|
||||
* @param index the scribe index
|
||||
*/
|
||||
public void setScribeIndex(ScribeIndex index) {
|
||||
this.index = index;
|
||||
serializer.setScribeIndex(index);
|
||||
deserializer.setScribeIndex(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timezone that all date/time property values will be formatted
|
||||
* in. If set, this setting will override the timezone information
|
||||
* associated with each {@link ICalendar} object.
|
||||
* @return the global timezone or null if not set (defaults to null)
|
||||
*/
|
||||
public TimezoneAssignment getGlobalTimezone() {
|
||||
return serializer.getGlobalTimezone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timezone that all date/time property values will be formatted
|
||||
* in. This is a convenience method that overrides the timezone information
|
||||
* associated with each {@link ICalendar} object that is passed into this
|
||||
* writer.
|
||||
* @param globalTimezone the global timezone or null not to set a global
|
||||
* timezone (defaults to null)
|
||||
*/
|
||||
public void setGlobalTimezone(TimezoneAssignment globalTimezone) {
|
||||
serializer.setGlobalTimezone(globalTimezone);
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
package biweekly.io.json;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Thrown during the parsing of a JSON-encoded iCalendar object (jCal) when the
|
||||
* jCal object is not formatted in the correct way (the JSON syntax is valid,
|
||||
* but it's not in the correct jCal format).
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class JCalParseException extends IOException {
|
||||
private static final long serialVersionUID = -2447563507966434472L;
|
||||
private final JsonToken expected, actual;
|
||||
|
||||
/**
|
||||
* Creates a jCal parse exception.
|
||||
* @param expected the JSON token that the parser was expecting
|
||||
* @param actual the actual JSON token
|
||||
*/
|
||||
public JCalParseException(JsonToken expected, JsonToken actual) {
|
||||
super("Expected " + expected + " but was " + actual + ".");
|
||||
this.expected = expected;
|
||||
this.actual = actual;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a jCal parse exception.
|
||||
* @param message the detail message
|
||||
* @param expected the JSON token that the parser was expecting
|
||||
* @param actual the actual JSON token
|
||||
*/
|
||||
public JCalParseException(String message, JsonToken expected, JsonToken actual) {
|
||||
super(message);
|
||||
this.expected = expected;
|
||||
this.actual = actual;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the JSON token that the parser was expected.
|
||||
* @return the expected token
|
||||
*/
|
||||
public JsonToken getExpectedToken() {
|
||||
return expected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the JSON token that was read.
|
||||
* @return the actual token
|
||||
*/
|
||||
public JsonToken getActualToken() {
|
||||
return actual;
|
||||
}
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
package biweekly.io.json;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerationException;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonStreamContext;
|
||||
import com.fasterxml.jackson.core.util.DefaultIndenter;
|
||||
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A JSON pretty-printer for jCals.
|
||||
* @author Buddy Gorven
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class JCalPrettyPrinter extends DefaultPrettyPrinter {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* The value that is assigned to {@link JsonGenerator#setCurrentValue} to
|
||||
* let the pretty-printer know that a iCalendar property is currently being
|
||||
* written.
|
||||
*/
|
||||
public static final Object PROPERTY_VALUE = "ical-property";
|
||||
|
||||
/**
|
||||
* Alias for {@link DefaultIndenter#SYSTEM_LINEFEED_INSTANCE}
|
||||
*/
|
||||
private static final Indenter NEWLINE_INDENTER = DefaultIndenter.SYSTEM_LINEFEED_INSTANCE;
|
||||
|
||||
/**
|
||||
* Instance of {@link DefaultPrettyPrinter.FixedSpaceIndenter}
|
||||
*/
|
||||
private static final Indenter INLINE_INDENTER = new DefaultPrettyPrinter.FixedSpaceIndenter();
|
||||
|
||||
private Indenter propertyIndenter, arrayIndenter, objectIndenter;
|
||||
|
||||
public JCalPrettyPrinter() {
|
||||
propertyIndenter = INLINE_INDENTER;
|
||||
indentArraysWith(NEWLINE_INDENTER);
|
||||
indentObjectsWith(NEWLINE_INDENTER);
|
||||
}
|
||||
|
||||
public JCalPrettyPrinter(JCalPrettyPrinter base) {
|
||||
super(base);
|
||||
propertyIndenter = base.propertyIndenter;
|
||||
indentArraysWith(base.arrayIndenter);
|
||||
indentObjectsWith(base.objectIndenter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JCalPrettyPrinter createInstance() {
|
||||
return new JCalPrettyPrinter(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void indentArraysWith(Indenter indenter) {
|
||||
arrayIndenter = indenter;
|
||||
super.indentArraysWith(indenter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void indentObjectsWith(Indenter indenter) {
|
||||
objectIndenter = indenter;
|
||||
super.indentObjectsWith(indenter);
|
||||
}
|
||||
|
||||
public void indentICalPropertiesWith(Indenter indenter) {
|
||||
propertyIndenter = indenter;
|
||||
}
|
||||
|
||||
protected static boolean isInICalProperty(JsonStreamContext context) {
|
||||
if (context == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object currentValue = context.getCurrentValue();
|
||||
if (currentValue == PROPERTY_VALUE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isInICalProperty(context.getParent());
|
||||
}
|
||||
|
||||
private void updateIndenter(JsonStreamContext context) {
|
||||
boolean inICalProperty = isInICalProperty(context);
|
||||
super.indentArraysWith(inICalProperty ? propertyIndenter : arrayIndenter);
|
||||
super.indentObjectsWith(inICalProperty ? propertyIndenter : objectIndenter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeStartArray(JsonGenerator gen) throws IOException, JsonGenerationException {
|
||||
updateIndenter(gen.getOutputContext().getParent());
|
||||
super.writeStartArray(gen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeEndArray(JsonGenerator gen, int numValues) throws IOException, JsonGenerationException {
|
||||
updateIndenter(gen.getOutputContext().getParent());
|
||||
super.writeEndArray(gen, numValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeArrayValueSeparator(JsonGenerator gen) throws IOException {
|
||||
updateIndenter(gen.getOutputContext().getParent());
|
||||
super.writeArrayValueSeparator(gen);
|
||||
}
|
||||
}
|
@ -1,335 +0,0 @@
|
||||
package biweekly.io.json;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import biweekly.ICalDataType;
|
||||
import biweekly.io.scribe.ScribeIndex;
|
||||
import biweekly.parameter.ICalParameters;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parses an iCalendar JSON data stream (jCal).
|
||||
* @author Michael Angstadt
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7265">RFC 7265</a>
|
||||
*/
|
||||
public class JCalRawReader implements Closeable {
|
||||
private static final String VCALENDAR_COMPONENT_NAME = ScribeIndex.getICalendarScribe().getComponentName().toLowerCase(); //"vcalendar"
|
||||
|
||||
private final Reader reader;
|
||||
private JsonParser parser;
|
||||
private boolean eof = false;
|
||||
private JCalDataStreamListener listener;
|
||||
private boolean strict = false;
|
||||
|
||||
/**
|
||||
* @param reader the reader to wrap
|
||||
*/
|
||||
public JCalRawReader(Reader reader) {
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parser the parser to read from
|
||||
* @param strict true if the parser's current token is expected to be
|
||||
* positioned at the start of a jCard, false if not. If this is true, and
|
||||
* the parser is not positioned at the beginning of a jCard, a
|
||||
* {@link JCalParseException} will be thrown. If this if false, the parser
|
||||
* will consume input until it reaches the beginning of a jCard.
|
||||
*/
|
||||
public JCalRawReader(JsonParser parser, boolean strict) {
|
||||
reader = null;
|
||||
this.parser = parser;
|
||||
this.strict = strict;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current line number.
|
||||
* @return the line number
|
||||
*/
|
||||
public int getLineNum() {
|
||||
return (parser == null) ? 0 : parser.getCurrentLocation().getLineNr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next iCalendar object from the jCal data stream.
|
||||
* @param listener handles the iCalendar data as it is read off the wire
|
||||
* @throws JCalParseException if the jCal syntax is incorrect (the JSON
|
||||
* syntax may be valid, but it is not in the correct jCal format).
|
||||
* @throws JsonParseException if the JSON syntax is incorrect
|
||||
* @throws IOException if there is a problem reading from the data stream
|
||||
*/
|
||||
public void readNext(JCalDataStreamListener listener) throws IOException {
|
||||
if (parser == null) {
|
||||
JsonFactory factory = new JsonFactory();
|
||||
parser = factory.createParser(reader);
|
||||
}
|
||||
|
||||
if (parser.isClosed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.listener = listener;
|
||||
|
||||
//find the next iCalendar object
|
||||
JsonToken prev = parser.getCurrentToken();
|
||||
JsonToken cur;
|
||||
while ((cur = parser.nextToken()) != null) {
|
||||
if (prev == JsonToken.START_ARRAY && cur == JsonToken.VALUE_STRING && VCALENDAR_COMPONENT_NAME.equals(parser.getValueAsString())) {
|
||||
//found
|
||||
break;
|
||||
}
|
||||
|
||||
if (strict) {
|
||||
//the parser was expecting the jCal to be there
|
||||
if (prev != JsonToken.START_ARRAY) {
|
||||
throw new JCalParseException(JsonToken.START_ARRAY, prev);
|
||||
}
|
||||
|
||||
if (cur != JsonToken.VALUE_STRING) {
|
||||
throw new JCalParseException(JsonToken.VALUE_STRING, cur);
|
||||
}
|
||||
|
||||
throw new JCalParseException("Invalid value for first token: expected \"vcalendar\" , was \"" + parser.getValueAsString() + "\"", JsonToken.VALUE_STRING, cur);
|
||||
}
|
||||
|
||||
prev = cur;
|
||||
}
|
||||
|
||||
if (cur == null) {
|
||||
//EOF
|
||||
eof = true;
|
||||
return;
|
||||
}
|
||||
|
||||
parseComponent(new ArrayList<String>());
|
||||
}
|
||||
|
||||
private void parseComponent(List<String> components) throws IOException {
|
||||
checkCurrent(JsonToken.VALUE_STRING);
|
||||
String componentName = parser.getValueAsString();
|
||||
listener.readComponent(components, componentName);
|
||||
components.add(componentName);
|
||||
|
||||
//start properties array
|
||||
checkNext(JsonToken.START_ARRAY);
|
||||
|
||||
//read properties
|
||||
while (parser.nextToken() != JsonToken.END_ARRAY) { //until we reach the end properties array
|
||||
checkCurrent(JsonToken.START_ARRAY);
|
||||
parser.nextToken();
|
||||
parseProperty(components);
|
||||
}
|
||||
|
||||
//start sub-components array
|
||||
checkNext(JsonToken.START_ARRAY);
|
||||
|
||||
//read sub-components
|
||||
while (parser.nextToken() != JsonToken.END_ARRAY) { //until we reach the end sub-components array
|
||||
checkCurrent(JsonToken.START_ARRAY);
|
||||
parser.nextToken();
|
||||
parseComponent(new ArrayList<String>(components));
|
||||
}
|
||||
|
||||
//read the end of the component array (e.g. the last bracket in this example: ["comp", [ /* props */ ], [ /* comps */] ])
|
||||
checkNext(JsonToken.END_ARRAY);
|
||||
}
|
||||
|
||||
private void parseProperty(List<String> components) throws IOException {
|
||||
//get property name
|
||||
checkCurrent(JsonToken.VALUE_STRING);
|
||||
String propertyName = parser.getValueAsString().toLowerCase();
|
||||
|
||||
ICalParameters parameters = parseParameters();
|
||||
|
||||
//get data type
|
||||
checkNext(JsonToken.VALUE_STRING);
|
||||
String dataTypeStr = parser.getText();
|
||||
ICalDataType dataType = "unknown".equals(dataTypeStr) ? null : ICalDataType.get(dataTypeStr);
|
||||
|
||||
//get property value(s)
|
||||
List<JsonValue> values = parseValues();
|
||||
|
||||
JCalValue value = new JCalValue(values);
|
||||
listener.readProperty(components, propertyName, parameters, dataType, value);
|
||||
}
|
||||
|
||||
private ICalParameters parseParameters() throws IOException {
|
||||
checkNext(JsonToken.START_OBJECT);
|
||||
|
||||
ICalParameters parameters = new ICalParameters();
|
||||
while (parser.nextToken() != JsonToken.END_OBJECT) {
|
||||
String parameterName = parser.getText();
|
||||
|
||||
if (parser.nextToken() == JsonToken.START_ARRAY) {
|
||||
//multi-valued parameter
|
||||
while (parser.nextToken() != JsonToken.END_ARRAY) {
|
||||
parameters.put(parameterName, parser.getText());
|
||||
}
|
||||
} else {
|
||||
parameters.put(parameterName, parser.getValueAsString());
|
||||
}
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private List<JsonValue> parseValues() throws IOException {
|
||||
List<JsonValue> values = new ArrayList<JsonValue>();
|
||||
while (parser.nextToken() != JsonToken.END_ARRAY) { //until we reach the end of the property array
|
||||
JsonValue value = parseValue();
|
||||
values.add(value);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private Object parseValueElement() throws IOException {
|
||||
switch (parser.getCurrentToken()) {
|
||||
case VALUE_FALSE:
|
||||
case VALUE_TRUE:
|
||||
return parser.getBooleanValue();
|
||||
case VALUE_NUMBER_FLOAT:
|
||||
return parser.getDoubleValue();
|
||||
case VALUE_NUMBER_INT:
|
||||
return parser.getLongValue();
|
||||
case VALUE_NULL:
|
||||
return null;
|
||||
default:
|
||||
return parser.getText();
|
||||
}
|
||||
}
|
||||
|
||||
private List<JsonValue> parseValueArray() throws IOException {
|
||||
List<JsonValue> array = new ArrayList<JsonValue>();
|
||||
|
||||
while (parser.nextToken() != JsonToken.END_ARRAY) {
|
||||
JsonValue value = parseValue();
|
||||
array.add(value);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
private Map<String, JsonValue> parseValueObject() throws IOException {
|
||||
Map<String, JsonValue> object = new HashMap<String, JsonValue>();
|
||||
|
||||
parser.nextToken();
|
||||
while (parser.getCurrentToken() != JsonToken.END_OBJECT) {
|
||||
checkCurrent(JsonToken.FIELD_NAME);
|
||||
|
||||
String key = parser.getText();
|
||||
parser.nextToken();
|
||||
JsonValue value = parseValue();
|
||||
object.put(key, value);
|
||||
|
||||
parser.nextToken();
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
private JsonValue parseValue() throws IOException {
|
||||
switch (parser.getCurrentToken()) {
|
||||
case START_ARRAY:
|
||||
return new JsonValue(parseValueArray());
|
||||
case START_OBJECT:
|
||||
return new JsonValue(parseValueObject());
|
||||
default:
|
||||
return new JsonValue(parseValueElement());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkNext(JsonToken expected) throws IOException {
|
||||
JsonToken actual = parser.nextToken();
|
||||
check(expected, actual);
|
||||
}
|
||||
|
||||
private void checkCurrent(JsonToken expected) throws JCalParseException {
|
||||
JsonToken actual = parser.getCurrentToken();
|
||||
check(expected, actual);
|
||||
}
|
||||
|
||||
private void check(JsonToken expected, JsonToken actual) throws JCalParseException {
|
||||
if (actual != expected) {
|
||||
throw new JCalParseException(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the end of the data stream has been reached.
|
||||
* @return true if the end has been reached, false if not
|
||||
*/
|
||||
public boolean eof() {
|
||||
return eof;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the iCalendar data as it is read off the data stream.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public interface JCalDataStreamListener {
|
||||
/**
|
||||
* Called when the parser begins to read a component.
|
||||
* @param parentHierarchy the component's parent components
|
||||
* @param componentName the component name (e.g. "vevent")
|
||||
*/
|
||||
void readComponent(List<String> parentHierarchy, String componentName);
|
||||
|
||||
/**
|
||||
* Called when a property is read.
|
||||
* @param componentHierarchy the hierarchy of components that the
|
||||
* property belongs to
|
||||
* @param propertyName the property name (e.g. "summary")
|
||||
* @param parameters the parameters
|
||||
* @param dataType the data type (e.g. "text")
|
||||
* @param value the property value
|
||||
*/
|
||||
void readProperty(List<String> componentHierarchy, String propertyName, ICalParameters parameters, ICalDataType dataType, JCalValue value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the underlying {@link Reader} object.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
if (parser != null) {
|
||||
parser.close();
|
||||
}
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,364 +0,0 @@
|
||||
package biweekly.io.json;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.Flushable;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import biweekly.ICalDataType;
|
||||
import biweekly.Messages;
|
||||
import biweekly.parameter.ICalParameters;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonGenerator.Feature;
|
||||
import com.fasterxml.jackson.core.PrettyPrinter;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Writes data to an iCalendar JSON data stream (jCal).
|
||||
* @author Michael Angstadt
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7265">RFC 7265</a>
|
||||
*/
|
||||
public class JCalRawWriter implements Closeable, Flushable {
|
||||
private final Writer writer;
|
||||
private final boolean wrapInArray;
|
||||
private final LinkedList<Info> stack = new LinkedList<Info>();
|
||||
private JsonGenerator generator;
|
||||
private boolean prettyPrint = false;
|
||||
private boolean componentEnded = false;
|
||||
private boolean closeGenerator = true;
|
||||
private PrettyPrinter prettyPrinter;
|
||||
|
||||
/**
|
||||
* @param writer the writer to wrap
|
||||
* @param wrapInArray true to wrap everything in an array, false not to
|
||||
* (useful when writing more than one iCalendar object)
|
||||
*/
|
||||
public JCalRawWriter(Writer writer, boolean wrapInArray) {
|
||||
this.writer = writer;
|
||||
this.wrapInArray = wrapInArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param generator the generator to write to
|
||||
*/
|
||||
public JCalRawWriter(JsonGenerator generator) {
|
||||
this.writer = null;
|
||||
this.generator = generator;
|
||||
this.closeGenerator = false;
|
||||
this.wrapInArray = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether or not the JSON will be pretty-printed.
|
||||
* @return true if it will be pretty-printed, false if not (defaults to
|
||||
* false)
|
||||
*/
|
||||
public boolean isPrettyPrint() {
|
||||
return prettyPrint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not to pretty-print the JSON.
|
||||
* @param prettyPrint true to pretty-print it, false not to (defaults to
|
||||
* false)
|
||||
*/
|
||||
public void setPrettyPrint(boolean prettyPrint) {
|
||||
this.prettyPrint = prettyPrint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pretty printer to pretty-print the JSON with. Note that this
|
||||
* method implicitly enables indenting, so {@code setPrettyPrint(true)} does
|
||||
* not also need to be called.
|
||||
* @param prettyPrinter the custom pretty printer (defaults to an instance
|
||||
* of {@link JCalPrettyPrinter}, if {@code setPrettyPrint(true)} has been
|
||||
* called)
|
||||
*/
|
||||
public void setPrettyPrinter(PrettyPrinter prettyPrinter) {
|
||||
prettyPrint = true;
|
||||
this.prettyPrinter = prettyPrinter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the beginning of a new component array.
|
||||
* @param componentName the component name (e.g. "vevent")
|
||||
* @throws IOException if there's an I/O problem
|
||||
*/
|
||||
public void writeStartComponent(String componentName) throws IOException {
|
||||
if (generator == null) {
|
||||
init();
|
||||
}
|
||||
|
||||
componentEnded = false;
|
||||
|
||||
if (!stack.isEmpty()) {
|
||||
Info parent = stack.getLast();
|
||||
if (!parent.wroteEndPropertiesArray) {
|
||||
generator.writeEndArray();
|
||||
parent.wroteEndPropertiesArray = true;
|
||||
}
|
||||
if (!parent.wroteStartSubComponentsArray) {
|
||||
generator.writeStartArray();
|
||||
parent.wroteStartSubComponentsArray = true;
|
||||
}
|
||||
}
|
||||
|
||||
generator.writeStartArray();
|
||||
generator.writeString(componentName);
|
||||
generator.writeStartArray(); //start properties array
|
||||
|
||||
stack.add(new Info());
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the current component array.
|
||||
* @throws IllegalStateException if there are no open components (
|
||||
* {@link #writeStartComponent(String)} must be called first)
|
||||
* @throws IOException if there's an I/O problem
|
||||
*/
|
||||
public void writeEndComponent() throws IOException {
|
||||
if (stack.isEmpty()) {
|
||||
throw new IllegalStateException(Messages.INSTANCE.getExceptionMessage(2));
|
||||
}
|
||||
Info cur = stack.removeLast();
|
||||
|
||||
if (!cur.wroteEndPropertiesArray) {
|
||||
generator.writeEndArray();
|
||||
}
|
||||
if (!cur.wroteStartSubComponentsArray) {
|
||||
generator.writeStartArray();
|
||||
}
|
||||
|
||||
generator.writeEndArray(); //end sub-components array
|
||||
generator.writeEndArray(); //end the array of this component
|
||||
|
||||
componentEnded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a property to the current component.
|
||||
* @param propertyName the property name (e.g. "version")
|
||||
* @param dataType the property's data type (e.g. "text")
|
||||
* @param value the property value
|
||||
* @throws IllegalStateException if there are no open components (
|
||||
* {@link #writeStartComponent(String)} must be called first) or if the last
|
||||
* method called was {@link #writeEndComponent()}.
|
||||
* @throws IOException if there's an I/O problem
|
||||
*/
|
||||
public void writeProperty(String propertyName, ICalDataType dataType, JCalValue value) throws IOException {
|
||||
writeProperty(propertyName, new ICalParameters(), dataType, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a property to the current component.
|
||||
* @param propertyName the property name (e.g. "version")
|
||||
* @param parameters the parameters
|
||||
* @param dataType the property's data type (e.g. "text")
|
||||
* @param value the property value
|
||||
* @throws IllegalStateException if there are no open components (
|
||||
* {@link #writeStartComponent(String)} must be called first) or if the last
|
||||
* method called was {@link #writeEndComponent()}.
|
||||
* @throws IOException if there's an I/O problem
|
||||
*/
|
||||
public void writeProperty(String propertyName, ICalParameters parameters, ICalDataType dataType, JCalValue value) throws IOException {
|
||||
if (stack.isEmpty()) {
|
||||
throw new IllegalStateException(Messages.INSTANCE.getExceptionMessage(2));
|
||||
}
|
||||
if (componentEnded) {
|
||||
throw new IllegalStateException(Messages.INSTANCE.getExceptionMessage(3));
|
||||
}
|
||||
|
||||
generator.setCurrentValue(JCalPrettyPrinter.PROPERTY_VALUE);
|
||||
|
||||
generator.writeStartArray();
|
||||
|
||||
//write the property name
|
||||
generator.writeString(propertyName);
|
||||
|
||||
//write parameters
|
||||
generator.writeStartObject();
|
||||
for (Map.Entry<String, List<String>> entry : parameters) {
|
||||
String name = entry.getKey().toLowerCase();
|
||||
List<String> values = entry.getValue();
|
||||
if (values.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (values.size() == 1) {
|
||||
generator.writeStringField(name, values.get(0));
|
||||
} else {
|
||||
generator.writeArrayFieldStart(name);
|
||||
for (String paramValue : values) {
|
||||
generator.writeString(paramValue);
|
||||
}
|
||||
generator.writeEndArray();
|
||||
}
|
||||
}
|
||||
generator.writeEndObject();
|
||||
|
||||
//write data type
|
||||
generator.writeString((dataType == null) ? "unknown" : dataType.getName().toLowerCase());
|
||||
|
||||
//write value
|
||||
for (JsonValue jsonValue : value.getValues()) {
|
||||
writeValue(jsonValue);
|
||||
}
|
||||
|
||||
generator.writeEndArray();
|
||||
|
||||
generator.setCurrentValue(null);
|
||||
}
|
||||
|
||||
private void writeValue(JsonValue jsonValue) throws IOException {
|
||||
if (jsonValue.isNull()) {
|
||||
generator.writeNull();
|
||||
return;
|
||||
}
|
||||
|
||||
Object val = jsonValue.getValue();
|
||||
if (val != null) {
|
||||
if (val instanceof Byte) {
|
||||
generator.writeNumber((Byte) val);
|
||||
} else if (val instanceof Short) {
|
||||
generator.writeNumber((Short) val);
|
||||
} else if (val instanceof Integer) {
|
||||
generator.writeNumber((Integer) val);
|
||||
} else if (val instanceof Long) {
|
||||
generator.writeNumber((Long) val);
|
||||
} else if (val instanceof Float) {
|
||||
generator.writeNumber((Float) val);
|
||||
} else if (val instanceof Double) {
|
||||
generator.writeNumber((Double) val);
|
||||
} else if (val instanceof Boolean) {
|
||||
generator.writeBoolean((Boolean) val);
|
||||
} else {
|
||||
generator.writeString(val.toString());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
List<JsonValue> array = jsonValue.getArray();
|
||||
if (array != null) {
|
||||
generator.writeStartArray();
|
||||
for (JsonValue element : array) {
|
||||
writeValue(element);
|
||||
}
|
||||
generator.writeEndArray();
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, JsonValue> object = jsonValue.getObject();
|
||||
if (object != null) {
|
||||
generator.writeStartObject();
|
||||
for (Map.Entry<String, JsonValue> entry : object.entrySet()) {
|
||||
generator.writeFieldName(entry.getKey());
|
||||
writeValue(entry.getValue());
|
||||
}
|
||||
generator.writeEndObject();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the JSON stream.
|
||||
* @throws IOException if there's a problem flushing the stream
|
||||
*/
|
||||
public void flush() throws IOException {
|
||||
if (generator == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
generator.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes writing the JSON document so that it is syntactically correct.
|
||||
* No more data can be written once this method is called.
|
||||
* @throws IOException if there's a problem closing the stream
|
||||
*/
|
||||
public void closeJsonStream() throws IOException {
|
||||
if (generator == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
writeEndComponent();
|
||||
}
|
||||
|
||||
if (wrapInArray) {
|
||||
generator.writeEndArray();
|
||||
}
|
||||
|
||||
if (closeGenerator) {
|
||||
generator.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes writing the JSON document and closes the underlying
|
||||
* {@link Writer}.
|
||||
* @throws IOException if there's a problem closing the stream
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
if (generator == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeJsonStream();
|
||||
|
||||
if (writer != null) {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void init() throws IOException {
|
||||
JsonFactory factory = new JsonFactory();
|
||||
factory.configure(Feature.AUTO_CLOSE_TARGET, false);
|
||||
generator = factory.createGenerator(writer);
|
||||
|
||||
if (prettyPrint) {
|
||||
if (prettyPrinter == null) {
|
||||
prettyPrinter = new JCalPrettyPrinter();
|
||||
}
|
||||
generator.setPrettyPrinter(prettyPrinter);
|
||||
}
|
||||
|
||||
if (wrapInArray) {
|
||||
generator.writeStartArray();
|
||||
}
|
||||
}
|
||||
|
||||
private static class Info {
|
||||
public boolean wroteEndPropertiesArray = false;
|
||||
public boolean wroteStartSubComponentsArray = false;
|
||||
}
|
||||
}
|
@ -1,241 +0,0 @@
|
||||
package biweekly.io.json;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
|
||||
import biweekly.ICalDataType;
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.ICalendar;
|
||||
import biweekly.component.ICalComponent;
|
||||
import biweekly.io.CannotParseException;
|
||||
import biweekly.io.ParseWarning;
|
||||
import biweekly.io.SkipMeException;
|
||||
import biweekly.io.StreamReader;
|
||||
import biweekly.io.json.JCalRawReader.JCalDataStreamListener;
|
||||
import biweekly.io.scribe.ScribeIndex;
|
||||
import biweekly.io.scribe.component.ICalComponentScribe;
|
||||
import biweekly.io.scribe.component.ICalendarScribe;
|
||||
import biweekly.io.scribe.property.ICalPropertyScribe;
|
||||
import biweekly.io.scribe.property.RawPropertyScribe;
|
||||
import biweekly.parameter.ICalParameters;
|
||||
import biweekly.property.ICalProperty;
|
||||
import biweekly.property.RawProperty;
|
||||
import biweekly.property.Version;
|
||||
import biweekly.util.Utf8Reader;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Parses {@link ICalendar} objects from a jCal data stream (JSON).
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Example:</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* File file = new File("icals.json");
|
||||
* JCalReader reader = null;
|
||||
* try {
|
||||
* reader = new JCalReader(file);
|
||||
* ICalendar ical;
|
||||
* while ((ical = reader.readNext()) != null) {
|
||||
* //...
|
||||
* }
|
||||
* } finally {
|
||||
* if (reader != null) reader.close();
|
||||
* }
|
||||
* </pre>
|
||||
* @author Michael Angstadt
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7265">RFC 7265</a>
|
||||
*/
|
||||
public class JCalReader extends StreamReader {
|
||||
private static final ICalendarScribe icalScribe = ScribeIndex.getICalendarScribe();
|
||||
private final JCalRawReader reader;
|
||||
|
||||
/**
|
||||
* @param json the JSON string to read from
|
||||
*/
|
||||
public JCalReader(String json) {
|
||||
this(new StringReader(json));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param in the input stream to read from
|
||||
*/
|
||||
public JCalReader(InputStream in) {
|
||||
this(new Utf8Reader(in));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file the file to read from
|
||||
* @throws FileNotFoundException if the file doesn't exist
|
||||
*/
|
||||
public JCalReader(File file) throws FileNotFoundException {
|
||||
this(new BufferedReader(new Utf8Reader(file)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param reader the reader to read from
|
||||
*/
|
||||
public JCalReader(Reader reader) {
|
||||
this.reader = new JCalRawReader(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parser the parser to read from
|
||||
*/
|
||||
public JCalReader(JsonParser parser) {
|
||||
this.reader = new JCalRawReader(parser, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next iCalendar object from the JSON data stream.
|
||||
* @return the iCalendar object or null if there are no more
|
||||
* @throws JCalParseException if the jCal syntax is incorrect (the JSON
|
||||
* syntax may be valid, but it is not in the correct jCal format).
|
||||
* @throws JsonParseException if the JSON syntax is incorrect
|
||||
* @throws IOException if there is a problem reading from the data stream
|
||||
*/
|
||||
@Override
|
||||
public ICalendar _readNext() throws IOException {
|
||||
if (reader.eof()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
context.setVersion(ICalVersion.V2_0);
|
||||
|
||||
JCalDataStreamListenerImpl listener = new JCalDataStreamListenerImpl();
|
||||
reader.readNext(listener);
|
||||
|
||||
return listener.getICalendar();
|
||||
}
|
||||
|
||||
//@Override
|
||||
public void close() throws IOException {
|
||||
reader.close();
|
||||
}
|
||||
|
||||
private class JCalDataStreamListenerImpl implements JCalDataStreamListener {
|
||||
private final Map<List<String>, ICalComponent> components = new HashMap<List<String>, ICalComponent>();
|
||||
|
||||
public void readProperty(List<String> componentHierarchy, String propertyName, ICalParameters parameters, ICalDataType dataType, JCalValue value) {
|
||||
context.getWarnings().clear();
|
||||
context.setLineNumber(reader.getLineNum());
|
||||
context.setPropertyName(propertyName);
|
||||
|
||||
//get the component that the property belongs to
|
||||
ICalComponent parent = components.get(componentHierarchy);
|
||||
|
||||
//unmarshal the property
|
||||
ICalPropertyScribe<? extends ICalProperty> scribe = index.getPropertyScribe(propertyName, ICalVersion.V2_0);
|
||||
try {
|
||||
ICalProperty property = scribe.parseJson(value, dataType, parameters, context);
|
||||
warnings.addAll(context.getWarnings());
|
||||
|
||||
//set "ICalendar.version" if the value of the VERSION property is recognized
|
||||
//otherwise, unmarshal VERSION like a normal property
|
||||
if (parent instanceof ICalendar && property instanceof Version) {
|
||||
Version version = (Version) property;
|
||||
ICalVersion icalVersion = version.toICalVersion();
|
||||
if (icalVersion != null) {
|
||||
context.setVersion(icalVersion);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
parent.addProperty(property);
|
||||
} catch (SkipMeException e) {
|
||||
//@formatter:off
|
||||
warnings.add(new ParseWarning.Builder(context)
|
||||
.message(0, e.getMessage())
|
||||
.build()
|
||||
);
|
||||
//@formatter:on
|
||||
} catch (CannotParseException e) {
|
||||
RawProperty property = new RawPropertyScribe(propertyName).parseJson(value, dataType, parameters, context);
|
||||
parent.addProperty(property);
|
||||
|
||||
//@formatter:off
|
||||
warnings.add(new ParseWarning.Builder(context)
|
||||
.message(e)
|
||||
.build()
|
||||
);
|
||||
//@formatter:on
|
||||
}
|
||||
}
|
||||
|
||||
public void readComponent(List<String> parentHierarchy, String componentName) {
|
||||
ICalComponentScribe<? extends ICalComponent> scribe = index.getComponentScribe(componentName, ICalVersion.V2_0);
|
||||
ICalComponent component = scribe.emptyInstance();
|
||||
|
||||
ICalComponent parent = components.get(parentHierarchy);
|
||||
if (parent != null) {
|
||||
parent.addComponent(component);
|
||||
}
|
||||
|
||||
List<String> hierarchy = new ArrayList<String>(parentHierarchy);
|
||||
hierarchy.add(componentName);
|
||||
components.put(hierarchy, component);
|
||||
}
|
||||
|
||||
public ICalendar getICalendar() {
|
||||
if (components.isEmpty()) {
|
||||
//EOF
|
||||
return null;
|
||||
}
|
||||
|
||||
ICalComponent component = components.get(Collections.singletonList(icalScribe.getComponentName().toLowerCase()));
|
||||
if (component == null) {
|
||||
//should never happen because the parser always looks for a "vcalendar" component
|
||||
return null;
|
||||
}
|
||||
|
||||
if (component instanceof ICalendar) {
|
||||
//should happen every time
|
||||
return (ICalendar) component;
|
||||
}
|
||||
|
||||
//this will only happen if the user decides to override the ICalendarScribe for some reason
|
||||
ICalendar ical = icalScribe.emptyInstance();
|
||||
ical.addComponent(component);
|
||||
return ical;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
package biweekly.io.json;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import biweekly.ICalendar;
|
||||
import biweekly.io.TimezoneAssignment;
|
||||
import biweekly.io.scribe.ScribeIndex;
|
||||
import biweekly.io.scribe.property.ICalPropertyScribe;
|
||||
import biweekly.property.ICalProperty;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Serializes jCals within the jackson-databind framework.
|
||||
* @author Buddy Gorven
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
@JsonFormat
|
||||
public class JCalSerializer extends StdSerializer<ICalendar> {
|
||||
private static final long serialVersionUID = 8964681078186049817L;
|
||||
private ScribeIndex index = new ScribeIndex();
|
||||
private TimezoneAssignment globalTimezone;
|
||||
|
||||
public JCalSerializer() {
|
||||
super(ICalendar.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(ICalendar value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
|
||||
@SuppressWarnings("resource")
|
||||
JCalWriter writer = new JCalWriter(gen);
|
||||
writer.setScribeIndex(getScribeIndex());
|
||||
writer.setGlobalTimezone(globalTimezone);
|
||||
writer.write(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Registers a property scribe. This is the same as calling:
|
||||
* </p>
|
||||
* <p>
|
||||
* {@code getScribeIndex().register(scribe)}
|
||||
* </p>
|
||||
* @param scribe the scribe to register
|
||||
*/
|
||||
public void registerScribe(ICalPropertyScribe<? extends ICalProperty> scribe) {
|
||||
index.register(scribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the scribe index.
|
||||
* @return the scribe index
|
||||
*/
|
||||
public ScribeIndex getScribeIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scribe index.
|
||||
* @param index the scribe index
|
||||
*/
|
||||
public void setScribeIndex(ScribeIndex index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timezone that all date/time property values will be formatted
|
||||
* in. If set, this setting will override the timezone information
|
||||
* associated with each {@link ICalendar} object.
|
||||
* @return the global timezone or null if not set (defaults to null)
|
||||
*/
|
||||
public TimezoneAssignment getGlobalTimezone() {
|
||||
return globalTimezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timezone that all date/time property values will be formatted
|
||||
* in. This is a convenience method that overrides the timezone information
|
||||
* associated with each {@link ICalendar} object that is passed into this
|
||||
* writer.
|
||||
* @param globalTimezone the global timezone or null not to set a global
|
||||
* timezone (defaults to null)
|
||||
*/
|
||||
public void setGlobalTimezone(TimezoneAssignment globalTimezone) {
|
||||
this.globalTimezone = globalTimezone;
|
||||
}
|
||||
}
|
@ -1,360 +0,0 @@
|
||||
package biweekly.io.json;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import biweekly.util.ListMultimap;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Holds the value of a jCal property.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class JCalValue {
|
||||
private final List<JsonValue> values;
|
||||
|
||||
/**
|
||||
* Creates a new jCal value.
|
||||
* @param values the values
|
||||
*/
|
||||
public JCalValue(List<JsonValue> values) {
|
||||
this.values = Collections.unmodifiableList(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new jCal value.
|
||||
* @param values the values
|
||||
*/
|
||||
public JCalValue(JsonValue... values) {
|
||||
this.values = Arrays.asList(values); //unmodifiable
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a single-valued value.
|
||||
* @param value the value
|
||||
* @return the jCal value
|
||||
*/
|
||||
public static JCalValue single(Object value) {
|
||||
return new JCalValue(new JsonValue(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a multi-valued value.
|
||||
* @param values the values
|
||||
* @return the jCal value
|
||||
*/
|
||||
public static JCalValue multi(Object... values) {
|
||||
return multi(Arrays.asList(values));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a multi-valued value.
|
||||
* @param values the values
|
||||
* @return the jCal value
|
||||
*/
|
||||
public static JCalValue multi(List<?> values) {
|
||||
List<JsonValue> multiValues = new ArrayList<JsonValue>(values.size());
|
||||
for (Object value : values) {
|
||||
multiValues.add(new JsonValue(value));
|
||||
}
|
||||
return new JCalValue(multiValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Creates a structured value.
|
||||
* </p>
|
||||
* <p>
|
||||
* This method accepts a vararg of {@link Object} instances. {@link List}
|
||||
* objects will be treated as multi-valued components. All other objects.
|
||||
* Null values will be treated as empty components.
|
||||
* </p>
|
||||
* @param values the values
|
||||
* @return the jCal value
|
||||
*/
|
||||
public static JCalValue structured(Object... values) {
|
||||
List<List<?>> valuesList = new ArrayList<List<?>>(values.length);
|
||||
for (Object value : values) {
|
||||
List<?> list = (value instanceof List) ? (List<?>) value : Collections.singletonList(value);
|
||||
valuesList.add(list);
|
||||
}
|
||||
return structured(valuesList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a structured value.
|
||||
* @param values the values
|
||||
* @return the jCal value
|
||||
*/
|
||||
public static JCalValue structured(List<List<?>> values) {
|
||||
List<JsonValue> array = new ArrayList<JsonValue>(values.size());
|
||||
|
||||
for (List<?> list : values) {
|
||||
if (list.isEmpty()) {
|
||||
array.add(new JsonValue(""));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (list.size() == 1) {
|
||||
Object value = list.get(0);
|
||||
if (value == null) {
|
||||
value = "";
|
||||
}
|
||||
array.add(new JsonValue(value));
|
||||
continue;
|
||||
}
|
||||
|
||||
List<JsonValue> subArray = new ArrayList<JsonValue>(list.size());
|
||||
for (Object value : list) {
|
||||
if (value == null) {
|
||||
value = "";
|
||||
}
|
||||
subArray.add(new JsonValue(value));
|
||||
}
|
||||
array.add(new JsonValue(subArray));
|
||||
}
|
||||
|
||||
return new JCalValue(new JsonValue(array));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an object value.
|
||||
* @param value the object
|
||||
* @return the jCal value
|
||||
*/
|
||||
public static JCalValue object(ListMultimap<String, Object> value) {
|
||||
Map<String, JsonValue> object = new LinkedHashMap<String, JsonValue>();
|
||||
for (Map.Entry<String, List<Object>> entry : value) {
|
||||
String key = entry.getKey();
|
||||
List<Object> list = entry.getValue();
|
||||
|
||||
JsonValue v;
|
||||
if (list.size() == 1) {
|
||||
v = new JsonValue(list.get(0));
|
||||
} else {
|
||||
List<JsonValue> array = new ArrayList<JsonValue>(list.size());
|
||||
for (Object element : list) {
|
||||
array.add(new JsonValue(element));
|
||||
}
|
||||
v = new JsonValue(array);
|
||||
}
|
||||
object.put(key, v);
|
||||
}
|
||||
return new JCalValue(new JsonValue(object));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw JSON values. Use one of the "{@code as*}" methods to parse
|
||||
* the values as one of the standard jCal values.
|
||||
* @return the JSON values
|
||||
*/
|
||||
public List<JsonValue> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses this jCal value as a single-valued property value.
|
||||
* @return the value or empty string if not found
|
||||
*/
|
||||
public String asSingle() {
|
||||
if (values.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
JsonValue first = values.get(0);
|
||||
if (first.isNull()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
Object obj = first.getValue();
|
||||
if (obj != null) {
|
||||
return obj.toString();
|
||||
}
|
||||
|
||||
//get the first element of the array
|
||||
List<JsonValue> array = first.getArray();
|
||||
if (array != null && !array.isEmpty()) {
|
||||
obj = array.get(0).getValue();
|
||||
if (obj != null) {
|
||||
return obj.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses this jCal value as a structured property value.
|
||||
* @return the structured values or empty list if not found
|
||||
*/
|
||||
public List<List<String>> asStructured() {
|
||||
if (values.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
JsonValue first = values.get(0);
|
||||
|
||||
//["request-status", {}, "text", ["2.0", "Success"] ]
|
||||
List<JsonValue> array = first.getArray();
|
||||
if (array != null) {
|
||||
List<List<String>> components = new ArrayList<List<String>>(array.size());
|
||||
for (JsonValue value : array) {
|
||||
if (value.isNull()) {
|
||||
components.add(Collections.<String>emptyList());
|
||||
continue;
|
||||
}
|
||||
|
||||
Object obj = value.getValue();
|
||||
if (obj != null) {
|
||||
String s = obj.toString();
|
||||
List<String> component = s.isEmpty() ? Collections.<String>emptyList() : Collections.singletonList(s);
|
||||
components.add(component);
|
||||
continue;
|
||||
}
|
||||
|
||||
List<JsonValue> subArray = value.getArray();
|
||||
if (subArray != null) {
|
||||
List<String> component = new ArrayList<String>(subArray.size());
|
||||
for (JsonValue subArrayValue : subArray) {
|
||||
if (subArrayValue.isNull()) {
|
||||
component.add("");
|
||||
continue;
|
||||
}
|
||||
|
||||
obj = subArrayValue.getValue();
|
||||
if (obj != null) {
|
||||
component.add(obj.toString());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (component.size() == 1 && component.get(0).isEmpty()) {
|
||||
component.clear();
|
||||
}
|
||||
components.add(component);
|
||||
}
|
||||
}
|
||||
return components;
|
||||
}
|
||||
|
||||
//get the first value if it's not enclosed in an array
|
||||
//["request-status", {}, "text", "2.0"]
|
||||
Object obj = first.getValue();
|
||||
if (obj != null) {
|
||||
List<List<String>> components = new ArrayList<List<String>>(1);
|
||||
String s = obj.toString();
|
||||
List<String> component = s.isEmpty() ? Collections.<String>emptyList() : Collections.singletonList(s);
|
||||
components.add(component);
|
||||
return components;
|
||||
}
|
||||
|
||||
//["request-status", {}, "text", null]
|
||||
if (first.isNull()) {
|
||||
List<List<String>> components = new ArrayList<List<String>>(1);
|
||||
components.add(Collections.<String>emptyList());
|
||||
return components;
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses this jCal value as a multi-valued property value.
|
||||
* @return the values or empty list if not found
|
||||
*/
|
||||
public List<String> asMulti() {
|
||||
if (values.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<String> multi = new ArrayList<String>(values.size());
|
||||
for (JsonValue value : values) {
|
||||
if (value.isNull()) {
|
||||
multi.add("");
|
||||
continue;
|
||||
}
|
||||
|
||||
Object obj = value.getValue();
|
||||
if (obj != null) {
|
||||
multi.add(obj.toString());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return multi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses this jCal value as an object property value.
|
||||
* @return the object or an empty map if not found
|
||||
*/
|
||||
public ListMultimap<String, String> asObject() {
|
||||
if (values.isEmpty()) {
|
||||
return new ListMultimap<String, String>(0);
|
||||
}
|
||||
|
||||
Map<String, JsonValue> map = values.get(0).getObject();
|
||||
if (map == null) {
|
||||
return new ListMultimap<String, String>(0);
|
||||
}
|
||||
|
||||
ListMultimap<String, String> values = new ListMultimap<String, String>();
|
||||
for (Map.Entry<String, JsonValue> entry : map.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
JsonValue value = entry.getValue();
|
||||
|
||||
if (value.isNull()) {
|
||||
values.put(key, "");
|
||||
continue;
|
||||
}
|
||||
|
||||
Object obj = value.getValue();
|
||||
if (obj != null) {
|
||||
values.put(key, obj.toString());
|
||||
continue;
|
||||
}
|
||||
|
||||
List<JsonValue> array = value.getArray();
|
||||
if (array != null) {
|
||||
for (JsonValue element : array) {
|
||||
if (element.isNull()) {
|
||||
values.put(key, "");
|
||||
continue;
|
||||
}
|
||||
|
||||
obj = element.getValue();
|
||||
if (obj != null) {
|
||||
values.put(key, obj.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
@ -1,250 +0,0 @@
|
||||
package biweekly.io.json;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Flushable;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import biweekly.ICalDataType;
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.ICalendar;
|
||||
import biweekly.component.ICalComponent;
|
||||
import biweekly.component.VTimezone;
|
||||
import biweekly.io.SkipMeException;
|
||||
import biweekly.io.StreamWriter;
|
||||
import biweekly.io.scribe.component.ICalComponentScribe;
|
||||
import biweekly.io.scribe.property.ICalPropertyScribe;
|
||||
import biweekly.parameter.ICalParameters;
|
||||
import biweekly.property.ICalProperty;
|
||||
import biweekly.property.Version;
|
||||
import biweekly.util.Utf8Writer;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Writes {@link ICalendar} objects to a JSON data stream (jCal).
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Example:</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* ICalendar ical1 = ...
|
||||
* ICalendar ical2 = ...
|
||||
* File file = new File("icals.json");
|
||||
* JCalWriter writer = null;
|
||||
* try {
|
||||
* writer = new JCalWriter(file);
|
||||
* writer.write(ical1);
|
||||
* writer.write(ical2);
|
||||
* } finally {
|
||||
* if (writer != null) writer.close();
|
||||
* }
|
||||
* </pre>
|
||||
* @author Michael Angstadt
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7265">RFC 7265</a>
|
||||
*/
|
||||
public class JCalWriter extends StreamWriter implements Flushable {
|
||||
private final JCalRawWriter writer;
|
||||
private final ICalVersion targetVersion = ICalVersion.V2_0;
|
||||
|
||||
/**
|
||||
* @param out the output stream to write to (UTF-8 encoding will be used)
|
||||
*/
|
||||
public JCalWriter(OutputStream out) {
|
||||
this(new Utf8Writer(out));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param out the output stream to write to (UTF-8 encoding will be used)
|
||||
* @param wrapInArray true to wrap all iCalendar objects in a parent array,
|
||||
* false not to (useful when writing more than one iCalendar object)
|
||||
*/
|
||||
public JCalWriter(OutputStream out, boolean wrapInArray) {
|
||||
this(new Utf8Writer(out), wrapInArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file the file to write to (UTF-8 encoding will be used)
|
||||
* @throws IOException if the file cannot be written to
|
||||
*/
|
||||
public JCalWriter(File file) throws IOException {
|
||||
this(new Utf8Writer(file));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file the file to write to (UTF-8 encoding will be used)
|
||||
* @param wrapInArray true to wrap all iCalendar objects in a parent array,
|
||||
* false not to (useful when writing more than one iCalendar object)
|
||||
* @throws IOException if the file cannot be written to
|
||||
*/
|
||||
public JCalWriter(File file, boolean wrapInArray) throws IOException {
|
||||
this(new Utf8Writer(file), wrapInArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param writer the writer to write to
|
||||
*/
|
||||
public JCalWriter(Writer writer) {
|
||||
this(writer, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param writer the writer to write to
|
||||
* @param wrapInArray true to wrap all iCalendar objects in a parent array,
|
||||
* false not to (useful when writing more than one iCalendar object)
|
||||
*/
|
||||
public JCalWriter(Writer writer, boolean wrapInArray) {
|
||||
this.writer = new JCalRawWriter(writer, wrapInArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param generator the generator to write to
|
||||
*/
|
||||
public JCalWriter(JsonGenerator generator) {
|
||||
this.writer = new JCalRawWriter(generator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether or not the JSON will be pretty-printed.
|
||||
* @return true if it will be pretty-printed, false if not (defaults to
|
||||
* false)
|
||||
*/
|
||||
public boolean isPrettyPrint() {
|
||||
return writer.isPrettyPrint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not to pretty-print the JSON.
|
||||
* @param prettyPrint true to pretty-print it, false not to (defaults to
|
||||
* false)
|
||||
*/
|
||||
public void setPrettyPrint(boolean prettyPrint) {
|
||||
writer.setPrettyPrint(prettyPrint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _write(ICalendar ical) throws IOException {
|
||||
writeComponent(ical);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ICalVersion getTargetVersion() {
|
||||
return targetVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a component to the data stream.
|
||||
* @param component the component to write
|
||||
* @throws IllegalArgumentException if the scribe class for a component or
|
||||
* property object cannot be found (only happens when an experimental
|
||||
* property/component scribe is not registered with the
|
||||
* {@code registerScribe} method.)
|
||||
* @throws IOException if there's a problem writing to the data stream
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private void writeComponent(ICalComponent component) throws IOException {
|
||||
ICalComponentScribe componentScribe = index.getComponentScribe(component);
|
||||
writer.writeStartComponent(componentScribe.getComponentName().toLowerCase());
|
||||
|
||||
List propertyObjs = componentScribe.getProperties(component);
|
||||
if (component instanceof ICalendar && component.getProperty(Version.class) == null) {
|
||||
propertyObjs.add(0, new Version(targetVersion));
|
||||
}
|
||||
|
||||
//write properties
|
||||
for (Object propertyObj : propertyObjs) {
|
||||
context.setParent(component); //set parent here incase a scribe resets the parent
|
||||
ICalProperty property = (ICalProperty) propertyObj;
|
||||
ICalPropertyScribe propertyScribe = index.getPropertyScribe(property);
|
||||
|
||||
//marshal property
|
||||
ICalParameters parameters;
|
||||
JCalValue value;
|
||||
try {
|
||||
parameters = propertyScribe.prepareParameters(property, context);
|
||||
value = propertyScribe.writeJson(property, context);
|
||||
} catch (SkipMeException e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//write property
|
||||
String propertyName = propertyScribe.getPropertyName(targetVersion).toLowerCase();
|
||||
ICalDataType dataType = propertyScribe.dataType(property, targetVersion);
|
||||
writer.writeProperty(propertyName, parameters, dataType, value);
|
||||
}
|
||||
|
||||
//write sub-components
|
||||
List subComponents = componentScribe.getComponents(component);
|
||||
if (component instanceof ICalendar) {
|
||||
//add the VTIMEZONE components that were auto-generated by TimezoneOptions
|
||||
Collection<VTimezone> tzs = getTimezoneComponents();
|
||||
for (VTimezone tz : tzs) {
|
||||
if (!subComponents.contains(tz)) {
|
||||
subComponents.add(0, tz);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Object subComponentObj : subComponents) {
|
||||
ICalComponent subComponent = (ICalComponent) subComponentObj;
|
||||
writeComponent(subComponent);
|
||||
}
|
||||
|
||||
writer.writeEndComponent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the stream.
|
||||
* @throws IOException if there's a problem flushing the stream
|
||||
*/
|
||||
public void flush() throws IOException {
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes writing the JSON document and closes the underlying
|
||||
* {@link Writer} object.
|
||||
* @throws IOException if there's a problem closing the stream
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
writer.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes writing the JSON document so that it is syntactically correct.
|
||||
* No more iCalendar objects can be written once this method is called.
|
||||
* @throws IOException if there's a problem writing to the data stream
|
||||
*/
|
||||
public void closeJsonStream() throws IOException {
|
||||
writer.closeJsonStream();
|
||||
}
|
||||
}
|
@ -1,166 +0,0 @@
|
||||
package biweekly.io.json;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a JSON value, array, or object.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class JsonValue {
|
||||
private final boolean isNull;
|
||||
private final Object value;
|
||||
private final List<JsonValue> array;
|
||||
private final Map<String, JsonValue> object;
|
||||
|
||||
/**
|
||||
* Creates a JSON value (such as a string or integer).
|
||||
* @param value the value
|
||||
*/
|
||||
public JsonValue(Object value) {
|
||||
this.value = value;
|
||||
array = null;
|
||||
object = null;
|
||||
isNull = (value == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON array.
|
||||
* @param array the array elements
|
||||
*/
|
||||
public JsonValue(List<JsonValue> array) {
|
||||
this.array = array;
|
||||
value = null;
|
||||
object = null;
|
||||
isNull = (array == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON object.
|
||||
* @param object the object fields
|
||||
*/
|
||||
public JsonValue(Map<String, JsonValue> object) {
|
||||
this.object = object;
|
||||
value = null;
|
||||
array = null;
|
||||
isNull = (object == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the JSON value.
|
||||
* @return the value or null if it's not a JSON value
|
||||
*/
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the JSON array elements.
|
||||
* @return the array elements or null if it's not a JSON array
|
||||
*/
|
||||
public List<JsonValue> getArray() {
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the JSON object.
|
||||
* @return the object or null if it's not a JSON object
|
||||
*/
|
||||
public Map<String, JsonValue> getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the value is "null" or not.
|
||||
* @return true if the value is "null", false if not
|
||||
*/
|
||||
public boolean isNull() {
|
||||
return isNull;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((array == null) ? 0 : array.hashCode());
|
||||
result = prime * result + (isNull ? 1231 : 1237);
|
||||
result = prime * result + ((object == null) ? 0 : object.hashCode());
|
||||
result = prime * result + ((value == null) ? 0 : value.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
JsonValue other = (JsonValue) obj;
|
||||
if (array == null) {
|
||||
if (other.array != null)
|
||||
return false;
|
||||
} else if (!array.equals(other.array))
|
||||
return false;
|
||||
if (isNull != other.isNull)
|
||||
return false;
|
||||
if (object == null) {
|
||||
if (other.object != null)
|
||||
return false;
|
||||
} else if (!object.equals(other.object))
|
||||
return false;
|
||||
if (value == null) {
|
||||
if (other.value != null)
|
||||
return false;
|
||||
} else if (!value.equals(other.value))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (isNull) {
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
return "VALUE = " + value;
|
||||
}
|
||||
|
||||
if (array != null) {
|
||||
return "ARRAY = " + array;
|
||||
}
|
||||
|
||||
if (object != null) {
|
||||
return "OBJECT = " + object;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
/**
|
||||
* Contains classes for reading and writing jCals (JSON-encoded iCalendar objects).
|
||||
*/
|
||||
package biweekly.io.json;
|
@ -1,4 +0,0 @@
|
||||
/**
|
||||
* Contains I/O related classes.
|
||||
*/
|
||||
package biweekly.io;
|
@ -1,451 +0,0 @@
|
||||
package biweekly.io.scribe;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.ICalendar;
|
||||
import biweekly.component.ICalComponent;
|
||||
import biweekly.component.RawComponent;
|
||||
import biweekly.io.scribe.component.DaylightSavingsTimeScribe;
|
||||
import biweekly.io.scribe.component.ICalComponentScribe;
|
||||
import biweekly.io.scribe.component.ICalendarScribe;
|
||||
import biweekly.io.scribe.component.RawComponentScribe;
|
||||
import biweekly.io.scribe.component.StandardTimeScribe;
|
||||
import biweekly.io.scribe.component.VAlarmScribe;
|
||||
import biweekly.io.scribe.component.VEventScribe;
|
||||
import biweekly.io.scribe.component.VFreeBusyScribe;
|
||||
import biweekly.io.scribe.component.VJournalScribe;
|
||||
import biweekly.io.scribe.component.VTimezoneScribe;
|
||||
import biweekly.io.scribe.component.VTodoScribe;
|
||||
import biweekly.io.scribe.property.ActionScribe;
|
||||
import biweekly.io.scribe.property.AttachmentScribe;
|
||||
import biweekly.io.scribe.property.AttendeeScribe;
|
||||
import biweekly.io.scribe.property.AudioAlarmScribe;
|
||||
import biweekly.io.scribe.property.CalendarScaleScribe;
|
||||
import biweekly.io.scribe.property.CategoriesScribe;
|
||||
import biweekly.io.scribe.property.ClassificationScribe;
|
||||
import biweekly.io.scribe.property.ColorScribe;
|
||||
import biweekly.io.scribe.property.CommentScribe;
|
||||
import biweekly.io.scribe.property.CompletedScribe;
|
||||
import biweekly.io.scribe.property.ConferenceScribe;
|
||||
import biweekly.io.scribe.property.ContactScribe;
|
||||
import biweekly.io.scribe.property.CreatedScribe;
|
||||
import biweekly.io.scribe.property.DateDueScribe;
|
||||
import biweekly.io.scribe.property.DateEndScribe;
|
||||
import biweekly.io.scribe.property.DateStartScribe;
|
||||
import biweekly.io.scribe.property.DateTimeStampScribe;
|
||||
import biweekly.io.scribe.property.DaylightScribe;
|
||||
import biweekly.io.scribe.property.DescriptionScribe;
|
||||
import biweekly.io.scribe.property.DisplayAlarmScribe;
|
||||
import biweekly.io.scribe.property.DurationPropertyScribe;
|
||||
import biweekly.io.scribe.property.EmailAlarmScribe;
|
||||
import biweekly.io.scribe.property.ExceptionDatesScribe;
|
||||
import biweekly.io.scribe.property.ExceptionRuleScribe;
|
||||
import biweekly.io.scribe.property.FreeBusyScribe;
|
||||
import biweekly.io.scribe.property.GeoScribe;
|
||||
import biweekly.io.scribe.property.ICalPropertyScribe;
|
||||
import biweekly.io.scribe.property.ImageScribe;
|
||||
import biweekly.io.scribe.property.LastModifiedScribe;
|
||||
import biweekly.io.scribe.property.LocationScribe;
|
||||
import biweekly.io.scribe.property.MethodScribe;
|
||||
import biweekly.io.scribe.property.NameScribe;
|
||||
import biweekly.io.scribe.property.OrganizerScribe;
|
||||
import biweekly.io.scribe.property.PercentCompleteScribe;
|
||||
import biweekly.io.scribe.property.PriorityScribe;
|
||||
import biweekly.io.scribe.property.ProcedureAlarmScribe;
|
||||
import biweekly.io.scribe.property.ProductIdScribe;
|
||||
import biweekly.io.scribe.property.RawPropertyScribe;
|
||||
import biweekly.io.scribe.property.RecurrenceDatesScribe;
|
||||
import biweekly.io.scribe.property.RecurrenceIdScribe;
|
||||
import biweekly.io.scribe.property.RecurrenceRuleScribe;
|
||||
import biweekly.io.scribe.property.RefreshIntervalScribe;
|
||||
import biweekly.io.scribe.property.RelatedToScribe;
|
||||
import biweekly.io.scribe.property.RepeatScribe;
|
||||
import biweekly.io.scribe.property.RequestStatusScribe;
|
||||
import biweekly.io.scribe.property.ResourcesScribe;
|
||||
import biweekly.io.scribe.property.SequenceScribe;
|
||||
import biweekly.io.scribe.property.SourceScribe;
|
||||
import biweekly.io.scribe.property.StatusScribe;
|
||||
import biweekly.io.scribe.property.SummaryScribe;
|
||||
import biweekly.io.scribe.property.TimezoneIdScribe;
|
||||
import biweekly.io.scribe.property.TimezoneNameScribe;
|
||||
import biweekly.io.scribe.property.TimezoneOffsetFromScribe;
|
||||
import biweekly.io.scribe.property.TimezoneOffsetToScribe;
|
||||
import biweekly.io.scribe.property.TimezoneScribe;
|
||||
import biweekly.io.scribe.property.TimezoneUrlScribe;
|
||||
import biweekly.io.scribe.property.TransparencyScribe;
|
||||
import biweekly.io.scribe.property.TriggerScribe;
|
||||
import biweekly.io.scribe.property.UidScribe;
|
||||
import biweekly.io.scribe.property.UrlScribe;
|
||||
import biweekly.io.scribe.property.VersionScribe;
|
||||
import biweekly.io.scribe.property.XmlScribe;
|
||||
import biweekly.io.xml.XCalNamespaceContext;
|
||||
import biweekly.property.ICalProperty;
|
||||
import biweekly.property.RawProperty;
|
||||
import biweekly.property.Xml;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Manages a listing of component and property scribes. This is useful for
|
||||
* injecting the scribes of any experimental components or properties you have
|
||||
* defined into a reader or writer object. The same ScribeIndex instance can be
|
||||
* reused and injected into multiple reader/writer classes.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Example:</b>
|
||||
* </p>
|
||||
*
|
||||
* <pre class="brush:java">
|
||||
* //init the index
|
||||
* ScribeIndex index = new ScribeIndex();
|
||||
* index.register(new CustomPropertyScribe());
|
||||
* index.register(new AnotherCustomPropertyScribe());
|
||||
* index.register(new CustomComponentScribe());
|
||||
*
|
||||
* //inject into a reader class
|
||||
* ICalReader reader = new ICalReader(...);
|
||||
* reader.setScribeIndex(index);
|
||||
* List<ICalendar> icals = new ArrayList<ICalendar>();
|
||||
* ICalendar ical;
|
||||
* while ((ical = reader.readNext()) != null) {
|
||||
* icals.add(ical);
|
||||
* }
|
||||
*
|
||||
* //inject the same instance in another reader/writer class
|
||||
* JCalWriter writer = new JCalWriter(...);
|
||||
* writer.setScribeIndex(index);
|
||||
* for (ICalendar ical : icals) {
|
||||
* writer.write(ical);
|
||||
* }
|
||||
* </pre>
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ScribeIndex {
|
||||
//define standard component scribes
|
||||
private static final Map<String, ICalComponentScribe<? extends ICalComponent>> standardCompByName = new HashMap<String, ICalComponentScribe<? extends ICalComponent>>();
|
||||
private static final Map<Class<? extends ICalComponent>, ICalComponentScribe<? extends ICalComponent>> standardCompByClass = new HashMap<Class<? extends ICalComponent>, ICalComponentScribe<? extends ICalComponent>>();
|
||||
static {
|
||||
registerStandard(new ICalendarScribe());
|
||||
registerStandard(new VAlarmScribe());
|
||||
registerStandard(new VEventScribe());
|
||||
registerStandard(new VFreeBusyScribe());
|
||||
registerStandard(new VJournalScribe());
|
||||
registerStandard(new VTodoScribe());
|
||||
registerStandard(new VTimezoneScribe());
|
||||
registerStandard(new StandardTimeScribe());
|
||||
registerStandard(new DaylightSavingsTimeScribe());
|
||||
}
|
||||
|
||||
//define standard property scribes
|
||||
private static final Map<String, ICalPropertyScribe<? extends ICalProperty>> standardPropByName = new HashMap<String, ICalPropertyScribe<? extends ICalProperty>>();
|
||||
private static final Map<Class<? extends ICalProperty>, ICalPropertyScribe<? extends ICalProperty>> standardPropByClass = new HashMap<Class<? extends ICalProperty>, ICalPropertyScribe<? extends ICalProperty>>();
|
||||
private static final Map<QName, ICalPropertyScribe<? extends ICalProperty>> standardPropByQName = new HashMap<QName, ICalPropertyScribe<? extends ICalProperty>>();
|
||||
static {
|
||||
//RFC 5545
|
||||
registerStandard(new ActionScribe());
|
||||
registerStandard(new AttachmentScribe());
|
||||
registerStandard(new AttendeeScribe());
|
||||
registerStandard(new CalendarScaleScribe());
|
||||
registerStandard(new CategoriesScribe());
|
||||
registerStandard(new ClassificationScribe());
|
||||
registerStandard(new CommentScribe());
|
||||
registerStandard(new CompletedScribe());
|
||||
registerStandard(new ContactScribe());
|
||||
registerStandard(new CreatedScribe());
|
||||
registerStandard(new DateDueScribe());
|
||||
registerStandard(new DateEndScribe());
|
||||
registerStandard(new DateStartScribe());
|
||||
registerStandard(new DateTimeStampScribe());
|
||||
registerStandard(new DescriptionScribe());
|
||||
registerStandard(new DurationPropertyScribe());
|
||||
registerStandard(new ExceptionDatesScribe());
|
||||
registerStandard(new FreeBusyScribe());
|
||||
registerStandard(new GeoScribe());
|
||||
registerStandard(new LastModifiedScribe());
|
||||
registerStandard(new LocationScribe());
|
||||
registerStandard(new MethodScribe());
|
||||
registerStandard(new OrganizerScribe());
|
||||
registerStandard(new PercentCompleteScribe());
|
||||
registerStandard(new PriorityScribe());
|
||||
registerStandard(new ProductIdScribe());
|
||||
registerStandard(new RecurrenceDatesScribe());
|
||||
registerStandard(new RecurrenceIdScribe());
|
||||
registerStandard(new RecurrenceRuleScribe());
|
||||
registerStandard(new RelatedToScribe());
|
||||
registerStandard(new RepeatScribe());
|
||||
registerStandard(new RequestStatusScribe());
|
||||
registerStandard(new ResourcesScribe());
|
||||
registerStandard(new SequenceScribe());
|
||||
registerStandard(new StatusScribe());
|
||||
registerStandard(new SummaryScribe());
|
||||
registerStandard(new TimezoneIdScribe());
|
||||
registerStandard(new TimezoneNameScribe());
|
||||
registerStandard(new TimezoneOffsetFromScribe());
|
||||
registerStandard(new TimezoneOffsetToScribe());
|
||||
registerStandard(new TimezoneUrlScribe());
|
||||
registerStandard(new TransparencyScribe());
|
||||
registerStandard(new TriggerScribe());
|
||||
registerStandard(new UidScribe());
|
||||
registerStandard(new UrlScribe());
|
||||
registerStandard(new VersionScribe());
|
||||
|
||||
//RFC 6321
|
||||
registerStandard(new XmlScribe());
|
||||
|
||||
//RFC 2445
|
||||
registerStandard(new ExceptionRuleScribe());
|
||||
|
||||
//vCal
|
||||
registerStandard(new AudioAlarmScribe());
|
||||
registerStandard(new DaylightScribe());
|
||||
registerStandard(new DisplayAlarmScribe());
|
||||
registerStandard(new EmailAlarmScribe());
|
||||
registerStandard(new ProcedureAlarmScribe());
|
||||
registerStandard(new TimezoneScribe());
|
||||
|
||||
//draft-ietf-calext-extensions-01
|
||||
registerStandard(new ColorScribe());
|
||||
registerStandard(new ConferenceScribe());
|
||||
registerStandard(new ImageScribe());
|
||||
registerStandard(new NameScribe());
|
||||
registerStandard(new SourceScribe());
|
||||
registerStandard(new RefreshIntervalScribe());
|
||||
}
|
||||
|
||||
private final Map<String, ICalComponentScribe<? extends ICalComponent>> experimentalCompByName = new HashMap<String, ICalComponentScribe<? extends ICalComponent>>(0);
|
||||
private final Map<Class<? extends ICalComponent>, ICalComponentScribe<? extends ICalComponent>> experimentalCompByClass = new HashMap<Class<? extends ICalComponent>, ICalComponentScribe<? extends ICalComponent>>(0);
|
||||
|
||||
private final Map<String, ICalPropertyScribe<? extends ICalProperty>> experimentalPropByName = new HashMap<String, ICalPropertyScribe<? extends ICalProperty>>(0);
|
||||
private final Map<Class<? extends ICalProperty>, ICalPropertyScribe<? extends ICalProperty>> experimentalPropByClass = new HashMap<Class<? extends ICalProperty>, ICalPropertyScribe<? extends ICalProperty>>(0);
|
||||
private final Map<QName, ICalPropertyScribe<? extends ICalProperty>> experimentalPropByQName = new HashMap<QName, ICalPropertyScribe<? extends ICalProperty>>(0);
|
||||
|
||||
/**
|
||||
* Gets a component scribe by name.
|
||||
* @param componentName the component name (e.g. "VEVENT")
|
||||
* @param version the version of the iCalendar object being parsed
|
||||
* @return the component scribe or a {@link RawComponentScribe} if not found
|
||||
*/
|
||||
public ICalComponentScribe<? extends ICalComponent> getComponentScribe(String componentName, ICalVersion version) {
|
||||
componentName = componentName.toUpperCase();
|
||||
|
||||
ICalComponentScribe<? extends ICalComponent> scribe = experimentalCompByName.get(componentName);
|
||||
if (scribe == null) {
|
||||
scribe = standardCompByName.get(componentName);
|
||||
}
|
||||
|
||||
if (scribe == null) {
|
||||
return new RawComponentScribe(componentName);
|
||||
}
|
||||
|
||||
if (version != null && !scribe.getSupportedVersions().contains(version)) {
|
||||
//treat the component as a raw component if the current iCal version doesn't support it
|
||||
return new RawComponentScribe(componentName);
|
||||
}
|
||||
|
||||
return scribe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a property scribe by name.
|
||||
* @param propertyName the property name (e.g. "UID")
|
||||
* @param version the version of the iCalendar object being parsed
|
||||
* @return the property scribe or a {@link RawPropertyScribe} if not found
|
||||
*/
|
||||
public ICalPropertyScribe<? extends ICalProperty> getPropertyScribe(String propertyName, ICalVersion version) {
|
||||
propertyName = propertyName.toUpperCase();
|
||||
|
||||
String key = propertyNameKey(propertyName, version);
|
||||
ICalPropertyScribe<? extends ICalProperty> scribe = experimentalPropByName.get(key);
|
||||
if (scribe == null) {
|
||||
scribe = standardPropByName.get(key);
|
||||
}
|
||||
|
||||
if (scribe == null) {
|
||||
return new RawPropertyScribe(propertyName);
|
||||
}
|
||||
|
||||
if (version != null && !scribe.getSupportedVersions().contains(version)) {
|
||||
//treat the property as a raw property if the current iCal version doesn't support it
|
||||
return new RawPropertyScribe(propertyName);
|
||||
}
|
||||
|
||||
return scribe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a component scribe by class.
|
||||
* @param clazz the component class
|
||||
* @return the component scribe or null if not found
|
||||
*/
|
||||
public ICalComponentScribe<? extends ICalComponent> getComponentScribe(Class<? extends ICalComponent> clazz) {
|
||||
ICalComponentScribe<? extends ICalComponent> scribe = experimentalCompByClass.get(clazz);
|
||||
if (scribe != null) {
|
||||
return scribe;
|
||||
}
|
||||
|
||||
return standardCompByClass.get(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a property scribe by class.
|
||||
* @param clazz the property class
|
||||
* @return the property scribe or null if not found
|
||||
*/
|
||||
public ICalPropertyScribe<? extends ICalProperty> getPropertyScribe(Class<? extends ICalProperty> clazz) {
|
||||
ICalPropertyScribe<? extends ICalProperty> scribe = experimentalPropByClass.get(clazz);
|
||||
if (scribe != null) {
|
||||
return scribe;
|
||||
}
|
||||
|
||||
return standardPropByClass.get(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the appropriate component scribe for a given component instance.
|
||||
* @param component the component instance
|
||||
* @return the component scribe or null if not found
|
||||
*/
|
||||
public ICalComponentScribe<? extends ICalComponent> getComponentScribe(ICalComponent component) {
|
||||
if (component instanceof RawComponent) {
|
||||
RawComponent raw = (RawComponent) component;
|
||||
return new RawComponentScribe(raw.getName());
|
||||
}
|
||||
|
||||
return getComponentScribe(component.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the appropriate property scribe for a given property instance.
|
||||
* @param property the property instance
|
||||
* @return the property scribe or null if not found
|
||||
*/
|
||||
public ICalPropertyScribe<? extends ICalProperty> getPropertyScribe(ICalProperty property) {
|
||||
if (property instanceof RawProperty) {
|
||||
RawProperty raw = (RawProperty) property;
|
||||
return new RawPropertyScribe(raw.getName());
|
||||
}
|
||||
|
||||
return getPropertyScribe(property.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a property scribe by XML local name and namespace.
|
||||
* @param qname the XML local name and namespace
|
||||
* @return the property scribe or a {@link XmlScribe} if not found
|
||||
*/
|
||||
public ICalPropertyScribe<? extends ICalProperty> getPropertyScribe(QName qname) {
|
||||
ICalPropertyScribe<? extends ICalProperty> scribe = experimentalPropByQName.get(qname);
|
||||
if (scribe == null) {
|
||||
scribe = standardPropByQName.get(qname);
|
||||
}
|
||||
|
||||
if (scribe == null || !scribe.getSupportedVersions().contains(ICalVersion.V2_0)) {
|
||||
if (XCalNamespaceContext.XCAL_NS.equals(qname.getNamespaceURI())) {
|
||||
return new RawPropertyScribe(qname.getLocalPart().toUpperCase());
|
||||
}
|
||||
return getPropertyScribe(Xml.class);
|
||||
}
|
||||
|
||||
return scribe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a component scribe.
|
||||
* @param scribe the scribe to register
|
||||
*/
|
||||
public void register(ICalComponentScribe<? extends ICalComponent> scribe) {
|
||||
experimentalCompByName.put(scribe.getComponentName().toUpperCase(), scribe);
|
||||
experimentalCompByClass.put(scribe.getComponentClass(), scribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a property scribe.
|
||||
* @param scribe the scribe to register
|
||||
*/
|
||||
public void register(ICalPropertyScribe<? extends ICalProperty> scribe) {
|
||||
for (ICalVersion version : ICalVersion.values()) {
|
||||
experimentalPropByName.put(propertyNameKey(scribe, version), scribe);
|
||||
}
|
||||
experimentalPropByClass.put(scribe.getPropertyClass(), scribe);
|
||||
experimentalPropByQName.put(scribe.getQName(), scribe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a component scribe.
|
||||
* @param scribe the scribe to unregister
|
||||
*/
|
||||
public void unregister(ICalComponentScribe<? extends ICalComponent> scribe) {
|
||||
experimentalCompByName.remove(scribe.getComponentName().toUpperCase());
|
||||
experimentalCompByClass.remove(scribe.getComponentClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a property scribe
|
||||
* @param scribe the scribe to unregister
|
||||
*/
|
||||
public void unregister(ICalPropertyScribe<? extends ICalProperty> scribe) {
|
||||
for (ICalVersion version : ICalVersion.values()) {
|
||||
experimentalPropByName.remove(propertyNameKey(scribe, version));
|
||||
}
|
||||
experimentalPropByClass.remove(scribe.getPropertyClass());
|
||||
experimentalPropByQName.remove(scribe.getQName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for getting the scribe of the root iCalendar component
|
||||
* ("VCALENDAR").
|
||||
* @return the scribe
|
||||
*/
|
||||
public static ICalendarScribe getICalendarScribe() {
|
||||
return (ICalendarScribe) standardCompByClass.get(ICalendar.class);
|
||||
}
|
||||
|
||||
private static void registerStandard(ICalComponentScribe<? extends ICalComponent> scribe) {
|
||||
standardCompByName.put(scribe.getComponentName().toUpperCase(), scribe);
|
||||
standardCompByClass.put(scribe.getComponentClass(), scribe);
|
||||
}
|
||||
|
||||
private static void registerStandard(ICalPropertyScribe<? extends ICalProperty> scribe) {
|
||||
for (ICalVersion version : ICalVersion.values()) {
|
||||
standardPropByName.put(propertyNameKey(scribe, version), scribe);
|
||||
}
|
||||
standardPropByClass.put(scribe.getPropertyClass(), scribe);
|
||||
standardPropByQName.put(scribe.getQName(), scribe);
|
||||
}
|
||||
|
||||
private static String propertyNameKey(ICalPropertyScribe<? extends ICalProperty> scribe, ICalVersion version) {
|
||||
return propertyNameKey(scribe.getPropertyName(version), version);
|
||||
}
|
||||
|
||||
private static String propertyNameKey(String propertyName, ICalVersion version) {
|
||||
return version.ordinal() + propertyName.toUpperCase();
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package biweekly.io.scribe.component;
|
||||
|
||||
import biweekly.component.DaylightSavingsTime;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class DaylightSavingsTimeScribe extends ObservanceScribe<DaylightSavingsTime> {
|
||||
public DaylightSavingsTimeScribe() {
|
||||
super(DaylightSavingsTime.class, "DAYLIGHT");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DaylightSavingsTime _newInstance() {
|
||||
return new DaylightSavingsTime();
|
||||
}
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
package biweekly.io.scribe.component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.component.ICalComponent;
|
||||
import biweekly.io.DataModelConversionException;
|
||||
import biweekly.property.ICalProperty;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base class for iCalendar component scribes.
|
||||
* @param <T> the component class
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public abstract class ICalComponentScribe<T extends ICalComponent> {
|
||||
private static final Set<ICalVersion> allVersions = Collections.unmodifiableSet(EnumSet.allOf(ICalVersion.class));
|
||||
|
||||
protected final Class<T> clazz;
|
||||
protected final String componentName;
|
||||
|
||||
/**
|
||||
* Creates a new component scribe.
|
||||
* @param clazz the component's class
|
||||
* @param componentName the component's name (e.g. "VEVENT")
|
||||
*/
|
||||
public ICalComponentScribe(Class<T> clazz, String componentName) {
|
||||
this.clazz = clazz;
|
||||
this.componentName = componentName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the iCalendar versions that support this component. This method
|
||||
* returns all iCalendar versions unless overridden by the child scribe.
|
||||
* @return the iCalendar versions
|
||||
*/
|
||||
public Set<ICalVersion> getSupportedVersions() {
|
||||
return allVersions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the component class.
|
||||
* @return the component class.
|
||||
*/
|
||||
public Class<T> getComponentClass() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the component's name.
|
||||
* @return the compent's name (e.g. "VEVENT")
|
||||
*/
|
||||
public String getComponentName() {
|
||||
return componentName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the component class that doesn't have any
|
||||
* properties or sub-components.
|
||||
* @return the new instance
|
||||
*/
|
||||
public T emptyInstance() {
|
||||
T component = _newInstance();
|
||||
|
||||
//remove any properties/components that were created in the constructor
|
||||
component.getProperties().clear();
|
||||
component.getComponents().clear();
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the component class.
|
||||
* @return the new instance
|
||||
*/
|
||||
protected abstract T _newInstance();
|
||||
|
||||
/**
|
||||
* Gets the sub-components to marshal. Child classes can override this for
|
||||
* better control over which components are marshalled.
|
||||
* @param component the component
|
||||
* @return the sub-components to marshal
|
||||
*/
|
||||
public List<ICalComponent> getComponents(T component) {
|
||||
return new ArrayList<ICalComponent>(component.getComponents().values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the properties to marshal. Child classes can override this for
|
||||
* better control over which properties are marshalled.
|
||||
* @param component the component
|
||||
* @return the properties to marshal
|
||||
*/
|
||||
public List<ICalProperty> getProperties(T component) {
|
||||
return new ArrayList<ICalProperty>(component.getProperties().values());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Checks this component to see if it needs to be converted to a different
|
||||
* data model before writing it out, throwing a
|
||||
* {@link DataModelConversionException} if it does.
|
||||
* </p>
|
||||
* <p>
|
||||
* Child classes should override this method if the component requires
|
||||
* any such conversion. The default implementation of this method does
|
||||
* nothing.
|
||||
* </p>
|
||||
* @param component the component being written
|
||||
* @param parent the component's parent or null if it has no parent
|
||||
* @param version the version iCalendar object being written
|
||||
* @throws DataModelConversionException if the component needs to be
|
||||
* converted
|
||||
*/
|
||||
public void checkForDataModelConversions(T component, ICalComponent parent, ICalVersion version) {
|
||||
//empty
|
||||
}
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
package biweekly.io.scribe.component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import biweekly.ICalendar;
|
||||
import biweekly.property.ICalProperty;
|
||||
import biweekly.property.ProductId;
|
||||
import biweekly.property.Version;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ICalendarScribe extends ICalComponentScribe<ICalendar> {
|
||||
public ICalendarScribe() {
|
||||
super(ICalendar.class, "VCALENDAR");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ICalendar _newInstance() {
|
||||
return new ICalendar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ICalProperty> getProperties(ICalendar component) {
|
||||
List<ICalProperty> properties = new ArrayList<ICalProperty>(component.getProperties().values());
|
||||
|
||||
/*
|
||||
* Move VERSION properties to the front (if any are present), followed
|
||||
* by PRODID properties. This is not required by the specs, but may help
|
||||
* with interoperability because all the examples in the specs put the
|
||||
* VERSION and PRODID at the very beginning of the iCalendar.
|
||||
*/
|
||||
moveToFront(ProductId.class, component, properties);
|
||||
moveToFront(Version.class, component, properties);
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
private <T extends ICalProperty> void moveToFront(Class<T> clazz, ICalendar component, List<ICalProperty> properties) {
|
||||
List<T> toMove = component.getProperties(clazz);
|
||||
properties.removeAll(toMove);
|
||||
properties.addAll(0, toMove);
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package biweekly.io.scribe.component;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.component.Observance;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public abstract class ObservanceScribe<T extends Observance> extends ICalComponentScribe<T> {
|
||||
protected ObservanceScribe(Class<T> clazz, String componentName) {
|
||||
super(clazz, componentName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ICalVersion> getSupportedVersions() {
|
||||
return EnumSet.of(ICalVersion.V2_0_DEPRECATED, ICalVersion.V2_0);
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package biweekly.io.scribe.component;
|
||||
|
||||
import biweekly.component.RawComponent;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class RawComponentScribe extends ICalComponentScribe<RawComponent> {
|
||||
/**
|
||||
* Creates a new raw component scribe.
|
||||
* @param componentName the component's name (e.g. "X-PARTY")
|
||||
*/
|
||||
public RawComponentScribe(String componentName) {
|
||||
super(RawComponent.class, componentName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RawComponent _newInstance() {
|
||||
return new RawComponent(componentName);
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package biweekly.io.scribe.component;
|
||||
|
||||
import biweekly.component.StandardTime;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class StandardTimeScribe extends ObservanceScribe<StandardTime> {
|
||||
public StandardTimeScribe() {
|
||||
super(StandardTime.class, "STANDARD");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StandardTime _newInstance() {
|
||||
return new StandardTime();
|
||||
}
|
||||
}
|
@ -1,230 +0,0 @@
|
||||
package biweekly.io.scribe.component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.component.ICalComponent;
|
||||
import biweekly.component.VAlarm;
|
||||
import biweekly.io.DataModelConversionException;
|
||||
import biweekly.parameter.Related;
|
||||
import biweekly.property.Action;
|
||||
import biweekly.property.Attachment;
|
||||
import biweekly.property.Attendee;
|
||||
import biweekly.property.AudioAlarm;
|
||||
import biweekly.property.DateEnd;
|
||||
import biweekly.property.DateStart;
|
||||
import biweekly.property.Description;
|
||||
import biweekly.property.DisplayAlarm;
|
||||
import biweekly.property.DurationProperty;
|
||||
import biweekly.property.EmailAlarm;
|
||||
import biweekly.property.ProcedureAlarm;
|
||||
import biweekly.property.Repeat;
|
||||
import biweekly.property.Trigger;
|
||||
import biweekly.property.VCalAlarmProperty;
|
||||
import biweekly.property.ValuedProperty;
|
||||
import biweekly.util.Duration;
|
||||
import biweekly.util.StringUtils;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class VAlarmScribe extends ICalComponentScribe<VAlarm> {
|
||||
public VAlarmScribe() {
|
||||
super(VAlarm.class, "VALARM");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VAlarm _newInstance() {
|
||||
return new VAlarm(null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkForDataModelConversions(VAlarm component, ICalComponent parent, ICalVersion version) {
|
||||
if (version != ICalVersion.V1_0) {
|
||||
return;
|
||||
}
|
||||
|
||||
VCalAlarmProperty vcalAlarm = convert(component, parent);
|
||||
if (vcalAlarm == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DataModelConversionException e = new DataModelConversionException(null);
|
||||
e.getProperties().add(vcalAlarm);
|
||||
throw e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ICalVersion> getSupportedVersions() {
|
||||
return EnumSet.of(ICalVersion.V2_0_DEPRECATED, ICalVersion.V2_0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link VAlarm} component to a vCal alarm property.
|
||||
* @param valarm the component
|
||||
* @param parent the component's parent
|
||||
* @return the vCal alarm property or null if it cannot be converted
|
||||
*/
|
||||
private static VCalAlarmProperty convert(VAlarm valarm, ICalComponent parent) {
|
||||
VCalAlarmProperty property = create(valarm);
|
||||
if (property == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
property.setStart(determineStartDate(valarm, parent));
|
||||
|
||||
DurationProperty duration = valarm.getDuration();
|
||||
if (duration != null) {
|
||||
property.setSnooze(duration.getValue());
|
||||
}
|
||||
|
||||
Repeat repeat = valarm.getRepeat();
|
||||
if (repeat != null) {
|
||||
property.setRepeat(repeat.getValue());
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link VCalAlarmProperty} based on the given {@link VAlarm}
|
||||
* component, setting fields that are common to all
|
||||
* {@link VCalAlarmProperty} classes.
|
||||
* @param valarm the source component
|
||||
* @return the property or null if it cannot be created
|
||||
*/
|
||||
private static VCalAlarmProperty create(VAlarm valarm) {
|
||||
Action action = valarm.getAction();
|
||||
if (action == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (action.isAudio()) {
|
||||
AudioAlarm aalarm = new AudioAlarm();
|
||||
|
||||
List<Attachment> attaches = valarm.getAttachments();
|
||||
if (!attaches.isEmpty()) {
|
||||
Attachment attach = attaches.get(0);
|
||||
|
||||
String formatType = attach.getFormatType();
|
||||
aalarm.setParameter("TYPE", formatType);
|
||||
|
||||
byte[] data = attach.getData();
|
||||
if (data != null) {
|
||||
aalarm.setData(data);
|
||||
}
|
||||
|
||||
String uri = attach.getUri();
|
||||
if (uri != null) {
|
||||
String contentId = StringUtils.afterPrefixIgnoreCase(uri, "cid:");
|
||||
if (contentId == null) {
|
||||
aalarm.setUri(uri);
|
||||
} else {
|
||||
aalarm.setContentId(contentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return aalarm;
|
||||
}
|
||||
|
||||
if (action.isDisplay()) {
|
||||
Description description = valarm.getDescription();
|
||||
String text = ValuedProperty.getValue(description);
|
||||
return new DisplayAlarm(text);
|
||||
}
|
||||
|
||||
if (action.isEmail()) {
|
||||
List<Attendee> attendees = valarm.getAttendees();
|
||||
String email = attendees.isEmpty() ? null : attendees.get(0).getEmail();
|
||||
EmailAlarm malarm = new EmailAlarm(email);
|
||||
|
||||
Description description = valarm.getDescription();
|
||||
String note = ValuedProperty.getValue(description);
|
||||
malarm.setNote(note);
|
||||
|
||||
return malarm;
|
||||
}
|
||||
|
||||
if (action.isProcedure()) {
|
||||
Description description = valarm.getDescription();
|
||||
String path = ValuedProperty.getValue(description);
|
||||
return new ProcedureAlarm(path);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines what the alarm property's start date should be.
|
||||
* @param valarm the component that is being converted to a vCal alarm
|
||||
* property
|
||||
* @param parent the component's parent
|
||||
* @return the start date or null if it cannot be determined
|
||||
*/
|
||||
private static Date determineStartDate(VAlarm valarm, ICalComponent parent) {
|
||||
Trigger trigger = valarm.getTrigger();
|
||||
if (trigger == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Date triggerStart = trigger.getDate();
|
||||
if (triggerStart != null) {
|
||||
return triggerStart;
|
||||
}
|
||||
|
||||
Duration triggerDuration = trigger.getDuration();
|
||||
if (triggerDuration == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Related related = trigger.getRelated();
|
||||
Date date = null;
|
||||
if (related == Related.START) {
|
||||
date = ValuedProperty.getValue(parent.getProperty(DateStart.class));
|
||||
} else if (related == Related.END) {
|
||||
date = ValuedProperty.getValue(parent.getProperty(DateEnd.class));
|
||||
if (date == null) {
|
||||
Date dateStart = ValuedProperty.getValue(parent.getProperty(DateStart.class));
|
||||
Duration duration = ValuedProperty.getValue(parent.getProperty(DurationProperty.class));
|
||||
if (duration != null && dateStart != null) {
|
||||
date = duration.add(dateStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (date == null) ? null : triggerDuration.add(date);
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package biweekly.io.scribe.component;
|
||||
|
||||
import biweekly.component.VEvent;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class VEventScribe extends ICalComponentScribe<VEvent> {
|
||||
public VEventScribe() {
|
||||
super(VEvent.class, "VEVENT");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VEvent _newInstance() {
|
||||
return new VEvent();
|
||||
}
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
package biweekly.io.scribe.component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.component.VFreeBusy;
|
||||
import biweekly.property.FreeBusy;
|
||||
import biweekly.property.ICalProperty;
|
||||
import biweekly.util.Period;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class VFreeBusyScribe extends ICalComponentScribe<VFreeBusy> {
|
||||
public VFreeBusyScribe() {
|
||||
super(VFreeBusy.class, "VFREEBUSY");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ICalProperty> getProperties(VFreeBusy component) {
|
||||
List<ICalProperty> properties = super.getProperties(component);
|
||||
|
||||
List<FreeBusy> fb = new ArrayList<FreeBusy>(component.getFreeBusy());
|
||||
if (fb.isEmpty()) {
|
||||
return properties;
|
||||
}
|
||||
|
||||
//sort FREEBUSY properties by start date (p.100)
|
||||
Collections.sort(fb, new Comparator<FreeBusy>() {
|
||||
public int compare(FreeBusy one, FreeBusy two) {
|
||||
Date oneStart = getEarliestStartDate(one);
|
||||
Date twoStart = getEarliestStartDate(two);
|
||||
if (oneStart == null && twoStart == null) {
|
||||
return 0;
|
||||
}
|
||||
if (oneStart == null) {
|
||||
return 1;
|
||||
}
|
||||
if (twoStart == null) {
|
||||
return -1;
|
||||
}
|
||||
return oneStart.compareTo(twoStart);
|
||||
}
|
||||
|
||||
private Date getEarliestStartDate(FreeBusy fb) {
|
||||
Date date = null;
|
||||
for (Period tp : fb.getValues()) {
|
||||
if (tp.getStartDate() == null) {
|
||||
continue;
|
||||
}
|
||||
if (date == null || date.compareTo(tp.getStartDate()) > 0) {
|
||||
date = tp.getStartDate();
|
||||
}
|
||||
}
|
||||
return date;
|
||||
}
|
||||
});
|
||||
|
||||
//find index of first FREEBUSY instance
|
||||
int index = 0;
|
||||
for (ICalProperty prop : properties) {
|
||||
if (prop instanceof FreeBusy) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
//remove and re-add the FREEBUSY instances in sorted order
|
||||
properties = new ArrayList<ICalProperty>(properties);
|
||||
for (FreeBusy f : fb) {
|
||||
properties.remove(f);
|
||||
properties.add(index++, f);
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VFreeBusy _newInstance() {
|
||||
return new VFreeBusy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ICalVersion> getSupportedVersions() {
|
||||
return EnumSet.of(ICalVersion.V2_0_DEPRECATED, ICalVersion.V2_0);
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
package biweekly.io.scribe.component;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.component.VJournal;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class VJournalScribe extends ICalComponentScribe<VJournal> {
|
||||
public VJournalScribe() {
|
||||
super(VJournal.class, "VJOURNAL");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VJournal _newInstance() {
|
||||
return new VJournal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ICalVersion> getSupportedVersions() {
|
||||
return EnumSet.of(ICalVersion.V2_0_DEPRECATED, ICalVersion.V2_0);
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
package biweekly.io.scribe.component;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.component.VTimezone;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class VTimezoneScribe extends ICalComponentScribe<VTimezone> {
|
||||
public VTimezoneScribe() {
|
||||
super(VTimezone.class, "VTIMEZONE");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VTimezone _newInstance() {
|
||||
return new VTimezone((String) null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ICalVersion> getSupportedVersions() {
|
||||
return EnumSet.of(ICalVersion.V2_0_DEPRECATED, ICalVersion.V2_0);
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package biweekly.io.scribe.component;
|
||||
|
||||
import biweekly.component.VTodo;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class VTodoScribe extends ICalComponentScribe<VTodo> {
|
||||
public VTodoScribe() {
|
||||
super(VTodo.class, "VTODO");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VTodo _newInstance() {
|
||||
return new VTodo();
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
/**
|
||||
* Contains classes that marshal and unmarshal components.
|
||||
*/
|
||||
package biweekly.io.scribe.component;
|
@ -1,4 +0,0 @@
|
||||
/**
|
||||
* Contains classes that marshal and unmarshal components and properties in various formats.
|
||||
*/
|
||||
package biweekly.io.scribe;
|
@ -1,52 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.property.Action;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals {@link Action} properties.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ActionScribe extends TextPropertyScribe<Action> {
|
||||
public ActionScribe() {
|
||||
super(Action.class, "ACTION");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Action newInstance(String value, ICalVersion version) {
|
||||
return new Action(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ICalVersion> getSupportedVersions() {
|
||||
return EnumSet.of(ICalVersion.V2_0_DEPRECATED, ICalVersion.V2_0);
|
||||
}
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import biweekly.ICalDataType;
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.io.WriteContext;
|
||||
import biweekly.io.json.JCalValue;
|
||||
import biweekly.io.xml.XCalElement;
|
||||
import biweekly.property.Attachment;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals {@link Attachment} properties.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class AttachmentScribe extends BinaryPropertyScribe<Attachment> {
|
||||
public AttachmentScribe() {
|
||||
super(Attachment.class, "ATTACH");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ICalDataType _dataType(Attachment property, ICalVersion version) {
|
||||
if (property.getContentId() != null) {
|
||||
return (version == ICalVersion.V1_0) ? ICalDataType.CONTENT_ID : ICalDataType.URI;
|
||||
}
|
||||
return super._dataType(property, version);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Attachment newInstance(byte[] data) {
|
||||
/*
|
||||
* Note: "formatType" will be set when the parameters are assigned to
|
||||
* the property object.
|
||||
*/
|
||||
return new Attachment(null, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Attachment newInstance(String value, ICalDataType dataType) {
|
||||
/*
|
||||
* Note: "formatType" will be set when the parameters are assigned to
|
||||
* the property object.
|
||||
*/
|
||||
|
||||
if (dataType == ICalDataType.CONTENT_ID) {
|
||||
String contentId = getCidUriValue(value);
|
||||
if (contentId == null) {
|
||||
contentId = value;
|
||||
}
|
||||
Attachment attach = new Attachment(null, (String) null);
|
||||
attach.setContentId(contentId);
|
||||
return attach;
|
||||
}
|
||||
|
||||
String contentId = getCidUriValue(value);
|
||||
if (contentId != null) {
|
||||
Attachment attach = new Attachment(null, (String) null);
|
||||
attach.setContentId(contentId);
|
||||
return attach;
|
||||
}
|
||||
|
||||
return new Attachment(null, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String _writeText(Attachment property, WriteContext context) {
|
||||
String contentId = property.getContentId();
|
||||
if (contentId != null) {
|
||||
return (context.getVersion() == ICalVersion.V1_0) ? '<' + contentId + '>' : "cid:" + contentId;
|
||||
}
|
||||
|
||||
return super._writeText(property, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _writeXml(Attachment property, XCalElement element, WriteContext context) {
|
||||
String contentId = property.getContentId();
|
||||
if (contentId != null) {
|
||||
element.append(ICalDataType.URI, "cid:" + contentId);
|
||||
return;
|
||||
}
|
||||
|
||||
super._writeXml(property, element, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JCalValue _writeJson(Attachment property, WriteContext context) {
|
||||
String contentId = property.getContentId();
|
||||
if (contentId != null) {
|
||||
return JCalValue.single("cid:" + contentId);
|
||||
}
|
||||
|
||||
return super._writeJson(property, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the given "cid" URI.
|
||||
* @param uri the "cid" URI
|
||||
* @return the URI value or null if the given string is not a "cid" URI
|
||||
*/
|
||||
private static String getCidUriValue(String uri) {
|
||||
int colon = uri.indexOf(':');
|
||||
if (colon == 3) {
|
||||
String scheme = uri.substring(0, colon);
|
||||
return "cid".equalsIgnoreCase(scheme) ? uri.substring(colon + 1) : null;
|
||||
}
|
||||
|
||||
if (uri.length() > 0 && uri.charAt(0) == '<' && uri.charAt(uri.length() - 1) == '>') {
|
||||
return uri.substring(1, uri.length() - 1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,340 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import biweekly.ICalDataType;
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.io.DataModelConversionException;
|
||||
import biweekly.io.ParseContext;
|
||||
import biweekly.io.WriteContext;
|
||||
import biweekly.parameter.ICalParameters;
|
||||
import biweekly.parameter.ParticipationLevel;
|
||||
import biweekly.parameter.ParticipationStatus;
|
||||
import biweekly.parameter.Role;
|
||||
import biweekly.property.Attendee;
|
||||
import biweekly.property.Organizer;
|
||||
|
||||
import com.github.mangstadt.vinnie.io.VObjectPropertyValues;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals {@link Attendee} properties.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class AttendeeScribe extends ICalPropertyScribe<Attendee> {
|
||||
public AttendeeScribe() {
|
||||
super(Attendee.class, "ATTENDEE");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ICalDataType _defaultDataType(ICalVersion version) {
|
||||
switch (version) {
|
||||
case V1_0:
|
||||
return null;
|
||||
default:
|
||||
return ICalDataType.CAL_ADDRESS;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ICalDataType _dataType(Attendee property, ICalVersion version) {
|
||||
if (version == ICalVersion.V1_0 && property.getUri() != null) {
|
||||
return ICalDataType.URL;
|
||||
}
|
||||
return defaultDataType(version);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ICalParameters _prepareParameters(Attendee property, WriteContext context) {
|
||||
/*
|
||||
* Note: Parameter values are assigned using "put()" instead of the
|
||||
* appropriate "setter" methods so that any existing parameter values
|
||||
* are not overwritten.
|
||||
*/
|
||||
|
||||
ICalParameters copy = new ICalParameters(property.getParameters());
|
||||
|
||||
//RSVP parameter
|
||||
//1.0 - Uses the values "YES" and "NO"
|
||||
//2.0 - Uses the values "TRUE" and "FALSE"
|
||||
Boolean rsvp = property.getRsvp();
|
||||
if (rsvp != null) {
|
||||
String value;
|
||||
switch (context.getVersion()) {
|
||||
case V1_0:
|
||||
value = rsvp ? "YES" : "NO";
|
||||
break;
|
||||
|
||||
default:
|
||||
value = rsvp ? "TRUE" : "FALSE";
|
||||
break;
|
||||
}
|
||||
|
||||
copy.put(ICalParameters.RSVP, value);
|
||||
}
|
||||
|
||||
//ROLE and EXPECT parameters
|
||||
//1.0 - Uses ROLE and EXPECT
|
||||
//2.0 - Uses only ROLE
|
||||
Role role = property.getRole();
|
||||
ParticipationLevel level = property.getParticipationLevel();
|
||||
switch (context.getVersion()) {
|
||||
case V1_0:
|
||||
if (role != null) {
|
||||
copy.put(ICalParameters.ROLE, role.getValue());
|
||||
}
|
||||
if (level != null) {
|
||||
copy.put(ICalParameters.EXPECT, level.getValue(context.getVersion()));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
String value = null;
|
||||
if (role == Role.CHAIR) {
|
||||
value = role.getValue();
|
||||
} else if (level != null) {
|
||||
value = level.getValue(context.getVersion());
|
||||
} else if (role != null) {
|
||||
value = role.getValue();
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
copy.put(ICalParameters.ROLE, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//PARTSTAT vs STATUS
|
||||
//1.0 - Calls the parameter "STATUS"
|
||||
//2.0 - Calls the parameter "PARTSTAT"
|
||||
ParticipationStatus partStat = property.getParticipationStatus();
|
||||
if (partStat != null) {
|
||||
String paramName;
|
||||
String paramValue;
|
||||
|
||||
switch (context.getVersion()) {
|
||||
case V1_0:
|
||||
paramName = ICalParameters.STATUS;
|
||||
paramValue = (partStat == ParticipationStatus.NEEDS_ACTION) ? "NEEDS ACTION" : partStat.getValue();
|
||||
break;
|
||||
|
||||
default:
|
||||
paramName = ICalParameters.PARTSTAT;
|
||||
paramValue = partStat.getValue();
|
||||
break;
|
||||
}
|
||||
|
||||
copy.put(paramName, paramValue);
|
||||
}
|
||||
|
||||
//CN parameter
|
||||
String name = property.getCommonName();
|
||||
if (name != null && context.getVersion() != ICalVersion.V1_0) {
|
||||
copy.put(ICalParameters.CN, name);
|
||||
}
|
||||
|
||||
//EMAIL parameter
|
||||
String uri = property.getUri();
|
||||
String email = property.getEmail();
|
||||
if (uri != null && email != null && context.getVersion() != ICalVersion.V1_0) {
|
||||
copy.put(ICalParameters.EMAIL, email);
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Attendee _parseText(String value, ICalDataType dataType, ICalParameters parameters, ParseContext context) {
|
||||
String uri = null, name = null, email = null;
|
||||
Boolean rsvp = null;
|
||||
Role role = null;
|
||||
ParticipationLevel participationLevel = null;
|
||||
ParticipationStatus participationStatus = null;
|
||||
|
||||
switch (context.getVersion()) {
|
||||
case V1_0:
|
||||
Iterator<String> it = parameters.get(ICalParameters.RSVP).iterator();
|
||||
while (it.hasNext()) {
|
||||
String rsvpStr = it.next();
|
||||
|
||||
if ("YES".equalsIgnoreCase(rsvpStr)) {
|
||||
rsvp = Boolean.TRUE;
|
||||
it.remove();
|
||||
break;
|
||||
}
|
||||
|
||||
if ("NO".equalsIgnoreCase(rsvpStr)) {
|
||||
rsvp = Boolean.FALSE;
|
||||
it.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String roleStr = parameters.first(ICalParameters.ROLE);
|
||||
if (roleStr != null) {
|
||||
role = Role.get(roleStr);
|
||||
parameters.remove(ICalParameters.ROLE, roleStr);
|
||||
}
|
||||
|
||||
String expectStr = parameters.getExpect();
|
||||
if (expectStr != null) {
|
||||
participationLevel = ParticipationLevel.get(expectStr);
|
||||
parameters.remove(ICalParameters.EXPECT, expectStr);
|
||||
}
|
||||
|
||||
String statusStr = parameters.getStatus();
|
||||
if (statusStr != null) {
|
||||
participationStatus = ParticipationStatus.get(statusStr);
|
||||
parameters.remove(ICalParameters.STATUS, statusStr);
|
||||
}
|
||||
|
||||
int bracketStart = value.lastIndexOf('<');
|
||||
int bracketEnd = value.lastIndexOf('>');
|
||||
if (bracketStart >= 0 && bracketEnd >= 0 && bracketStart < bracketEnd) {
|
||||
name = value.substring(0, bracketStart).trim();
|
||||
email = value.substring(bracketStart + 1, bracketEnd).trim();
|
||||
} else if (dataType == ICalDataType.URL) {
|
||||
uri = value;
|
||||
} else {
|
||||
email = value;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
it = parameters.get(ICalParameters.RSVP).iterator();
|
||||
while (it.hasNext()) {
|
||||
String rsvpStr = it.next();
|
||||
|
||||
if ("TRUE".equalsIgnoreCase(rsvpStr)) {
|
||||
rsvp = Boolean.TRUE;
|
||||
it.remove();
|
||||
break;
|
||||
}
|
||||
|
||||
if ("FALSE".equalsIgnoreCase(rsvpStr)) {
|
||||
rsvp = Boolean.FALSE;
|
||||
it.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
roleStr = parameters.first(ICalParameters.ROLE);
|
||||
if (roleStr != null) {
|
||||
if (roleStr.equalsIgnoreCase(Role.CHAIR.getValue())) {
|
||||
role = Role.CHAIR;
|
||||
} else {
|
||||
ParticipationLevel l = ParticipationLevel.find(roleStr);
|
||||
if (l == null) {
|
||||
role = Role.get(roleStr);
|
||||
} else {
|
||||
participationLevel = l;
|
||||
}
|
||||
}
|
||||
parameters.remove(ICalParameters.ROLE, roleStr);
|
||||
}
|
||||
|
||||
String participationStatusStr = parameters.getParticipationStatus();
|
||||
if (participationStatusStr != null) {
|
||||
participationStatus = ParticipationStatus.get(participationStatusStr);
|
||||
parameters.remove(ICalParameters.PARTSTAT, participationStatusStr);
|
||||
}
|
||||
|
||||
name = parameters.getCommonName();
|
||||
if (name != null) {
|
||||
parameters.remove(ICalParameters.CN, name);
|
||||
}
|
||||
|
||||
email = parameters.getEmail();
|
||||
if (email == null) {
|
||||
int colon = value.indexOf(':');
|
||||
if (colon == 6) {
|
||||
String scheme = value.substring(0, colon);
|
||||
if (scheme.equalsIgnoreCase("mailto")) {
|
||||
email = value.substring(colon + 1);
|
||||
} else {
|
||||
uri = value;
|
||||
}
|
||||
} else {
|
||||
uri = value;
|
||||
}
|
||||
} else {
|
||||
uri = value;
|
||||
parameters.remove(ICalParameters.EMAIL, email);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Attendee attendee = new Attendee(name, email, uri);
|
||||
attendee.setParticipationStatus(participationStatus);
|
||||
attendee.setParticipationLevel(participationLevel);
|
||||
attendee.setRole(role);
|
||||
attendee.setRsvp(rsvp);
|
||||
|
||||
if (context.getVersion() == ICalVersion.V1_0 && attendee.getRole() == Role.ORGANIZER) {
|
||||
Organizer organizer = new Organizer(attendee.getCommonName(), attendee.getEmail());
|
||||
organizer.setUri(attendee.getUri());
|
||||
organizer.setParameters(parameters);
|
||||
|
||||
attendee.setParameters(parameters);
|
||||
DataModelConversionException conversionException = new DataModelConversionException(attendee);
|
||||
conversionException.getProperties().add(organizer);
|
||||
throw conversionException;
|
||||
}
|
||||
|
||||
return attendee;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String _writeText(Attendee property, WriteContext context) {
|
||||
String uri = property.getUri();
|
||||
if (uri != null) {
|
||||
return uri;
|
||||
}
|
||||
|
||||
String name = property.getCommonName();
|
||||
String email = property.getEmail();
|
||||
switch (context.getVersion()) {
|
||||
case V1_0:
|
||||
if (email != null) {
|
||||
String value = (name == null) ? email : name + " <" + email + ">";
|
||||
return VObjectPropertyValues.escape(value);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
if (email != null) {
|
||||
return "mailto:" + email;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
@ -1,145 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import biweekly.ICalDataType;
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.component.VAlarm;
|
||||
import biweekly.property.Action;
|
||||
import biweekly.property.Attachment;
|
||||
import biweekly.property.AudioAlarm;
|
||||
import biweekly.util.org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import com.github.mangstadt.vinnie.io.VObjectPropertyValues.SemiStructuredValueIterator;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals {@link AudioAlarm} properties.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class AudioAlarmScribe extends VCalAlarmPropertyScribe<AudioAlarm> {
|
||||
public AudioAlarmScribe() {
|
||||
super(AudioAlarm.class, "AALARM");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ICalDataType _dataType(AudioAlarm property, ICalVersion version) {
|
||||
if (property.getUri() != null) {
|
||||
return ICalDataType.URL;
|
||||
}
|
||||
if (property.getData() != null) {
|
||||
return ICalDataType.BINARY;
|
||||
}
|
||||
if (property.getContentId() != null) {
|
||||
return ICalDataType.CONTENT_ID;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> writeData(AudioAlarm property) {
|
||||
String uri = property.getUri();
|
||||
if (uri != null) {
|
||||
return Collections.singletonList(uri);
|
||||
}
|
||||
|
||||
byte[] data = property.getData();
|
||||
if (data != null) {
|
||||
String base64Str = Base64.encodeBase64String(data);
|
||||
return Collections.singletonList(base64Str);
|
||||
}
|
||||
|
||||
String contentId = property.getContentId();
|
||||
if (contentId != null) {
|
||||
return Collections.singletonList(contentId);
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AudioAlarm create(ICalDataType dataType, SemiStructuredValueIterator it) {
|
||||
AudioAlarm aalarm = new AudioAlarm();
|
||||
String next = it.next();
|
||||
if (next == null) {
|
||||
return aalarm;
|
||||
}
|
||||
|
||||
if (dataType == ICalDataType.BINARY) {
|
||||
byte[] data = Base64.decodeBase64(next);
|
||||
aalarm.setData(data);
|
||||
} else if (dataType == ICalDataType.URL) {
|
||||
aalarm.setUri(next);
|
||||
} else if (dataType == ICalDataType.CONTENT_ID) {
|
||||
aalarm.setContentId(next);
|
||||
} else {
|
||||
aalarm.setUri(next);
|
||||
}
|
||||
|
||||
return aalarm;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toVAlarm(VAlarm valarm, AudioAlarm property) {
|
||||
Attachment attach = buildAttachment(property);
|
||||
if (attach != null) {
|
||||
valarm.addAttachment(attach);
|
||||
}
|
||||
}
|
||||
|
||||
private static Attachment buildAttachment(AudioAlarm aalarm) {
|
||||
String type = aalarm.getType();
|
||||
String contentType = (type == null) ? null : "audio/" + type.toLowerCase();
|
||||
Attachment attach = new Attachment(contentType, (String) null);
|
||||
|
||||
byte[] data = aalarm.getData();
|
||||
if (data != null) {
|
||||
attach.setData(data);
|
||||
return attach;
|
||||
}
|
||||
|
||||
String contentId = aalarm.getContentId();
|
||||
if (contentId != null) {
|
||||
attach.setContentId(contentId);
|
||||
return attach;
|
||||
}
|
||||
|
||||
String uri = aalarm.getUri();
|
||||
if (uri != null) {
|
||||
attach.setUri(uri);
|
||||
return attach;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Action action() {
|
||||
return Action.audio();
|
||||
}
|
||||
}
|
@ -1,175 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import biweekly.ICalDataType;
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.io.ParseContext;
|
||||
import biweekly.io.WriteContext;
|
||||
import biweekly.io.json.JCalValue;
|
||||
import biweekly.io.xml.XCalElement;
|
||||
import biweekly.parameter.Encoding;
|
||||
import biweekly.parameter.ICalParameters;
|
||||
import biweekly.property.BinaryProperty;
|
||||
import biweekly.util.org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import com.github.mangstadt.vinnie.io.VObjectPropertyValues;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals {@link BinaryProperty} properties.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public abstract class BinaryPropertyScribe<T extends BinaryProperty> extends ICalPropertyScribe<T> {
|
||||
public BinaryPropertyScribe(Class<T> clazz, String propertyName) {
|
||||
super(clazz, propertyName, ICalDataType.URI);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ICalParameters _prepareParameters(T property, WriteContext context) {
|
||||
ICalParameters copy = new ICalParameters(property.getParameters());
|
||||
|
||||
if (property.getUri() != null) {
|
||||
copy.setEncoding(null);
|
||||
} else if (property.getData() != null) {
|
||||
copy.setEncoding(Encoding.BASE64);
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ICalDataType _dataType(T property, ICalVersion version) {
|
||||
if (property.getUri() != null) {
|
||||
return (version == ICalVersion.V1_0) ? ICalDataType.URL : ICalDataType.URI;
|
||||
}
|
||||
if (property.getData() != null) {
|
||||
return ICalDataType.BINARY;
|
||||
}
|
||||
return defaultDataType(version);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String _writeText(T property, WriteContext context) {
|
||||
String uri = property.getUri();
|
||||
if (uri != null) {
|
||||
return uri;
|
||||
}
|
||||
|
||||
byte[] data = property.getData();
|
||||
if (data != null) {
|
||||
return Base64.encodeBase64String(data);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T _parseText(String value, ICalDataType dataType, ICalParameters parameters, ParseContext context) {
|
||||
value = VObjectPropertyValues.unescape(value);
|
||||
|
||||
if (dataType == ICalDataType.BINARY || parameters.getEncoding() == Encoding.BASE64) {
|
||||
byte[] data = Base64.decodeBase64(value);
|
||||
return newInstance(data);
|
||||
}
|
||||
|
||||
return newInstance(value, dataType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _writeXml(T property, XCalElement element, WriteContext context) {
|
||||
String uri = property.getUri();
|
||||
if (uri != null) {
|
||||
element.append(ICalDataType.URI, uri);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] data = property.getData();
|
||||
if (data != null) {
|
||||
element.append(ICalDataType.BINARY, Base64.encodeBase64String(data));
|
||||
return;
|
||||
}
|
||||
|
||||
element.append(defaultDataType(context.getVersion()), "");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T _parseXml(XCalElement element, ICalParameters parameters, ParseContext context) {
|
||||
String uri = element.first(ICalDataType.URI);
|
||||
if (uri != null) {
|
||||
return newInstance(uri, ICalDataType.URI);
|
||||
}
|
||||
|
||||
String base64Data = element.first(ICalDataType.BINARY);
|
||||
if (base64Data != null) {
|
||||
byte[] data = Base64.decodeBase64(base64Data);
|
||||
return newInstance(data);
|
||||
}
|
||||
|
||||
throw missingXmlElements(ICalDataType.URI, ICalDataType.BINARY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JCalValue _writeJson(T property, WriteContext context) {
|
||||
String uri = property.getUri();
|
||||
if (uri != null) {
|
||||
return JCalValue.single(uri);
|
||||
}
|
||||
|
||||
byte[] data = property.getData();
|
||||
if (data != null) {
|
||||
return JCalValue.single(Base64.encodeBase64String(data));
|
||||
}
|
||||
|
||||
return JCalValue.single("");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T _parseJson(JCalValue value, ICalDataType dataType, ICalParameters parameters, ParseContext context) {
|
||||
String valueStr = value.asSingle();
|
||||
|
||||
if (dataType == ICalDataType.BINARY) {
|
||||
byte[] data = Base64.decodeBase64(valueStr);
|
||||
return newInstance(data);
|
||||
}
|
||||
|
||||
return newInstance(valueStr, dataType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a property object from the given binary data.
|
||||
* @param data the data
|
||||
* @return the property object
|
||||
*/
|
||||
protected abstract T newInstance(byte[] data);
|
||||
|
||||
/**
|
||||
* Creates a property object from the given string value.
|
||||
* @param value the string value
|
||||
* @param dataType the data type
|
||||
* @return the property object
|
||||
*/
|
||||
protected abstract T newInstance(String value, ICalDataType dataType);
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.property.CalendarScale;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals {@link CalendarScale} properties.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class CalendarScaleScribe extends TextPropertyScribe<CalendarScale> {
|
||||
public CalendarScaleScribe() {
|
||||
super(CalendarScale.class, "CALSCALE");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CalendarScale newInstance(String value, ICalVersion version) {
|
||||
return new CalendarScale(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ICalVersion> getSupportedVersions() {
|
||||
return EnumSet.of(ICalVersion.V2_0_DEPRECATED, ICalVersion.V2_0);
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import biweekly.ICalDataType;
|
||||
import biweekly.parameter.ICalParameters;
|
||||
import biweekly.property.Categories;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals {@link Categories} properties.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class CategoriesScribe extends TextListPropertyScribe<Categories> {
|
||||
public CategoriesScribe() {
|
||||
super(Categories.class, "CATEGORIES");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Categories newInstance(ICalDataType dataType, ICalParameters parameters) {
|
||||
return new Categories();
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.property.Classification;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals {@link Classification} properties.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ClassificationScribe extends TextPropertyScribe<Classification> {
|
||||
public ClassificationScribe() {
|
||||
super(Classification.class, "CLASS");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Classification newInstance(String value, ICalVersion version) {
|
||||
return new Classification(value);
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.property.Color;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals {@link Color} properties.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ColorScribe extends TextPropertyScribe<Color> {
|
||||
public ColorScribe() {
|
||||
super(Color.class, "COLOR");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Color newInstance(String value, ICalVersion version) {
|
||||
return new Color(value);
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.property.Comment;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals {@link Comment} properties.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class CommentScribe extends TextPropertyScribe<Comment> {
|
||||
public CommentScribe() {
|
||||
super(Comment.class, "COMMENT");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Comment newInstance(String value, ICalVersion version) {
|
||||
return new Comment(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ICalVersion> getSupportedVersions() {
|
||||
return EnumSet.of(ICalVersion.V2_0_DEPRECATED, ICalVersion.V2_0);
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import biweekly.property.Completed;
|
||||
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals {@link Completed} properties.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class CompletedScribe extends DateTimePropertyScribe<Completed> {
|
||||
public CompletedScribe() {
|
||||
super(Completed.class, "COMPLETED");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Completed newInstance(Date date) {
|
||||
return new Completed(date);
|
||||
}
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import biweekly.ICalDataType;
|
||||
import biweekly.io.ParseContext;
|
||||
import biweekly.io.WriteContext;
|
||||
import biweekly.io.json.JCalValue;
|
||||
import biweekly.io.xml.XCalElement;
|
||||
import biweekly.parameter.ICalParameters;
|
||||
import biweekly.property.Conference;
|
||||
import biweekly.util.DataUri;
|
||||
|
||||
import com.github.mangstadt.vinnie.io.VObjectPropertyValues;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals {@link Conference} properties.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ConferenceScribe extends ICalPropertyScribe<Conference> {
|
||||
public ConferenceScribe() {
|
||||
super(Conference.class, "CONFERENCE", ICalDataType.URI);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String _writeText(Conference property, WriteContext context) {
|
||||
return write(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Conference _parseText(String value, ICalDataType dataType, ICalParameters parameters, ParseContext context) {
|
||||
value = VObjectPropertyValues.unescape(value);
|
||||
return parse(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _writeXml(Conference property, XCalElement element, WriteContext context) {
|
||||
element.append(ICalDataType.URI, write(property));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Conference _parseXml(XCalElement element, ICalParameters parameters, ParseContext context) {
|
||||
String uri = element.first(ICalDataType.URI);
|
||||
if (uri != null) {
|
||||
return parse(uri);
|
||||
}
|
||||
|
||||
throw missingXmlElements(ICalDataType.URI);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JCalValue _writeJson(Conference property, WriteContext context) {
|
||||
return JCalValue.single(write(property));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Conference _parseJson(JCalValue value, ICalDataType dataType, ICalParameters parameters, ParseContext context) {
|
||||
String uri = value.asSingle();
|
||||
return parse(uri);
|
||||
}
|
||||
|
||||
private static String write(Conference property) {
|
||||
String uri = property.getUri();
|
||||
if (uri != null) {
|
||||
return uri;
|
||||
}
|
||||
|
||||
String text = property.getText();
|
||||
if (text != null) {
|
||||
return new DataUri(text).toString();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private static Conference parse(String value) {
|
||||
try {
|
||||
DataUri uri = DataUri.parse(value);
|
||||
String text = uri.getText();
|
||||
if (text != null) {
|
||||
Conference property = new Conference((String) null);
|
||||
property.setText(text);
|
||||
return property;
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
//not a data URI
|
||||
}
|
||||
|
||||
return new Conference(value);
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.property.Contact;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals {@link Contact} properties.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class ContactScribe extends TextPropertyScribe<Contact> {
|
||||
public ContactScribe() {
|
||||
super(Contact.class, "CONTACT");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Contact newInstance(String value, ICalVersion version) {
|
||||
return new Contact(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ICalVersion> getSupportedVersions() {
|
||||
return EnumSet.of(ICalVersion.V2_0_DEPRECATED, ICalVersion.V2_0);
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.property.Created;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals {@link Created} properties.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class CreatedScribe extends DateTimePropertyScribe<Created> {
|
||||
public CreatedScribe() {
|
||||
super(Created.class, "CREATED");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPropertyName(ICalVersion version) {
|
||||
return (version == ICalVersion.V1_0) ? "DCREATED" : super.getPropertyName(version);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Created newInstance(Date date) {
|
||||
return new Created(date);
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import biweekly.property.DateDue;
|
||||
import biweekly.util.ICalDate;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals {@link DateDue} properties.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class DateDueScribe extends DateOrDateTimePropertyScribe<DateDue> {
|
||||
public DateDueScribe() {
|
||||
super(DateDue.class, "DUE");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DateDue newInstance(ICalDate date) {
|
||||
return new DateDue(date);
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import biweekly.property.DateEnd;
|
||||
import biweekly.util.ICalDate;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals {@link DateEnd} properties.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class DateEndScribe extends DateOrDateTimePropertyScribe<DateEnd> {
|
||||
public DateEndScribe() {
|
||||
super(DateEnd.class, "DTEND");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DateEnd newInstance(ICalDate date) {
|
||||
return new DateEnd(date);
|
||||
}
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import biweekly.ICalDataType;
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.io.CannotParseException;
|
||||
import biweekly.io.ParseContext;
|
||||
import biweekly.io.WriteContext;
|
||||
import biweekly.io.json.JCalValue;
|
||||
import biweekly.io.xml.XCalElement;
|
||||
import biweekly.parameter.ICalParameters;
|
||||
import biweekly.property.DateOrDateTimeProperty;
|
||||
import biweekly.util.ICalDate;
|
||||
|
||||
import com.github.mangstadt.vinnie.io.VObjectPropertyValues;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals properties that have either "date" or "date-time" values.
|
||||
* @param <T> the property class
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public abstract class DateOrDateTimePropertyScribe<T extends DateOrDateTimeProperty> extends ICalPropertyScribe<T> {
|
||||
public DateOrDateTimePropertyScribe(Class<T> clazz, String propertyName) {
|
||||
super(clazz, propertyName, ICalDataType.DATE_TIME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ICalParameters _prepareParameters(T property, WriteContext context) {
|
||||
ICalDate value = property.getValue();
|
||||
if (value == null) {
|
||||
return property.getParameters();
|
||||
}
|
||||
|
||||
return handleTzidParameter(property, value.hasTime(), context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ICalDataType _dataType(T property, ICalVersion version) {
|
||||
ICalDate value = property.getValue();
|
||||
if (value == null) {
|
||||
return ICalDataType.DATE_TIME;
|
||||
}
|
||||
|
||||
return value.hasTime() ? ICalDataType.DATE_TIME : ICalDataType.DATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String _writeText(T property, WriteContext context) {
|
||||
ICalDate value = property.getValue();
|
||||
return date(value, property, context).extended(false).write();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T _parseText(String value, ICalDataType dataType, ICalParameters parameters, ParseContext context) {
|
||||
value = VObjectPropertyValues.unescape(value);
|
||||
return parse(value, parameters, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _writeXml(T property, XCalElement element, WriteContext context) {
|
||||
ICalDataType dataType = dataType(property, null);
|
||||
ICalDate value = property.getValue();
|
||||
|
||||
String dateStr = date(value, property, context).extended(true).write();
|
||||
element.append(dataType, dateStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T _parseXml(XCalElement element, ICalParameters parameters, ParseContext context) {
|
||||
String value = element.first(ICalDataType.DATE_TIME);
|
||||
if (value == null) {
|
||||
value = element.first(ICalDataType.DATE);
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
return parse(value, parameters, context);
|
||||
}
|
||||
|
||||
throw missingXmlElements(ICalDataType.DATE_TIME, ICalDataType.DATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JCalValue _writeJson(T property, WriteContext context) {
|
||||
ICalDate value = property.getValue();
|
||||
String dateStr = date(value, property, context).extended(true).write();
|
||||
return JCalValue.single(dateStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T _parseJson(JCalValue value, ICalDataType dataType, ICalParameters parameters, ParseContext context) {
|
||||
String valueStr = value.asSingle();
|
||||
return parse(valueStr, parameters, context);
|
||||
}
|
||||
|
||||
protected abstract T newInstance(ICalDate date);
|
||||
|
||||
private T parse(String value, ICalParameters parameters, ParseContext context) {
|
||||
if (value == null) {
|
||||
return newInstance(null);
|
||||
}
|
||||
|
||||
ICalDate date;
|
||||
try {
|
||||
date = date(value).parse();
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new CannotParseException(17);
|
||||
}
|
||||
|
||||
T property = newInstance(date);
|
||||
context.addDate(date, property, parameters);
|
||||
return property;
|
||||
}
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import biweekly.ICalDataType;
|
||||
import biweekly.io.WriteContext;
|
||||
import biweekly.io.json.JCalValue;
|
||||
import biweekly.io.xml.XCalElement;
|
||||
import biweekly.parameter.ICalParameters;
|
||||
import biweekly.property.DateStart;
|
||||
import biweekly.util.ICalDate;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals {@link DateStart} properties.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class DateStartScribe extends DateOrDateTimePropertyScribe<DateStart> {
|
||||
public DateStartScribe() {
|
||||
super(DateStart.class, "DTSTART");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ICalParameters _prepareParameters(DateStart property, WriteContext context) {
|
||||
if (isInObservance(context)) {
|
||||
return property.getParameters();
|
||||
}
|
||||
return super._prepareParameters(property, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String _writeText(DateStart property, WriteContext context) {
|
||||
if (isInObservance(context)) {
|
||||
return write(property, false);
|
||||
}
|
||||
return super._writeText(property, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _writeXml(DateStart property, XCalElement element, WriteContext context) {
|
||||
if (isInObservance(context)) {
|
||||
String dateStr = write(property, true);
|
||||
ICalDataType dataType = dataType(property, null);
|
||||
element.append(dataType, dateStr);
|
||||
return;
|
||||
}
|
||||
|
||||
super._writeXml(property, element, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JCalValue _writeJson(DateStart property, WriteContext context) {
|
||||
if (isInObservance(context)) {
|
||||
return JCalValue.single(write(property, true));
|
||||
}
|
||||
return super._writeJson(property, context);
|
||||
}
|
||||
|
||||
private String write(DateStart property, boolean extended) {
|
||||
ICalDate value = property.getValue();
|
||||
return date(value).observance(true).extended(extended).write();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DateStart newInstance(ICalDate date) {
|
||||
return new DateStart(date);
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import biweekly.ICalDataType;
|
||||
import biweekly.io.CannotParseException;
|
||||
import biweekly.io.ParseContext;
|
||||
import biweekly.io.WriteContext;
|
||||
import biweekly.io.json.JCalValue;
|
||||
import biweekly.io.xml.XCalElement;
|
||||
import biweekly.parameter.ICalParameters;
|
||||
import biweekly.property.DateTimeProperty;
|
||||
import biweekly.util.ICalDate;
|
||||
|
||||
import com.github.mangstadt.vinnie.io.VObjectPropertyValues;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals properties that have "date-time" values. These values will always be
|
||||
* formatted in the UTC timezone.
|
||||
* @param <T> the property class
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public abstract class DateTimePropertyScribe<T extends DateTimeProperty> extends ICalPropertyScribe<T> {
|
||||
public DateTimePropertyScribe(Class<T> clazz, String propertyName) {
|
||||
super(clazz, propertyName, ICalDataType.DATE_TIME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String _writeText(T property, WriteContext context) {
|
||||
Date value = property.getValue();
|
||||
return date(value).utc(true).extended(false).write();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T _parseText(String value, ICalDataType dataType, ICalParameters parameters, ParseContext context) {
|
||||
value = VObjectPropertyValues.unescape(value);
|
||||
return parse(value, parameters, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _writeXml(T property, XCalElement element, WriteContext context) {
|
||||
ICalDataType dataType = dataType(property, null);
|
||||
Date value = property.getValue();
|
||||
String dateStr = date(value).utc(true).extended(true).write();
|
||||
|
||||
element.append(dataType, dateStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T _parseXml(XCalElement element, ICalParameters parameters, ParseContext context) {
|
||||
ICalDataType dataType = defaultDataType(context.getVersion());
|
||||
String value = element.first(dataType);
|
||||
if (value != null) {
|
||||
return parse(value, parameters, context);
|
||||
}
|
||||
|
||||
throw missingXmlElements(dataType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JCalValue _writeJson(T property, WriteContext context) {
|
||||
Date value = property.getValue();
|
||||
String dateStr = date(value).utc(true).extended(true).write();
|
||||
return JCalValue.single(dateStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T _parseJson(JCalValue value, ICalDataType dataType, ICalParameters parameters, ParseContext context) {
|
||||
String valueStr = value.asSingle();
|
||||
return parse(valueStr, parameters, context);
|
||||
}
|
||||
|
||||
private T parse(String value, ICalParameters parameters, ParseContext context) {
|
||||
ICalDate date;
|
||||
try {
|
||||
date = date(value).parse();
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new CannotParseException(17);
|
||||
}
|
||||
|
||||
T property = newInstance(date);
|
||||
context.addDate(date, property, parameters);
|
||||
return property;
|
||||
}
|
||||
|
||||
protected abstract T newInstance(Date date);
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
package biweekly.io.scribe.property;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
import biweekly.ICalVersion;
|
||||
import biweekly.io.SkipMeException;
|
||||
import biweekly.io.WriteContext;
|
||||
import biweekly.property.DateTimeStamp;
|
||||
|
||||
/*
|
||||
Copyright (c) 2013-2023, Michael Angstadt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marshals {@link DateTimeStamp} properties.
|
||||
* @author Michael Angstadt
|
||||
*/
|
||||
public class DateTimeStampScribe extends DateTimePropertyScribe<DateTimeStamp> {
|
||||
public DateTimeStampScribe() {
|
||||
super(DateTimeStamp.class, "DTSTAMP");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DateTimeStamp newInstance(Date date) {
|
||||
return new DateTimeStamp(date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ICalVersion> getSupportedVersions() {
|
||||
return EnumSet.of(ICalVersion.V2_0_DEPRECATED, ICalVersion.V2_0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String _writeText(DateTimeStamp property, WriteContext context) {
|
||||
if (context.getVersion() == ICalVersion.V1_0){
|
||||
throw new SkipMeException("This property is not used in vCal 1.0.");
|
||||
}
|
||||
return super._writeText(property, context);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue