-
Notifications
You must be signed in to change notification settings - Fork 44
Nonstandard Properties
iCalendar objects can contain non-standard properties that are not part of the iCal specification. These are called "experimental" properties.
The iCalendar object below contains an example of such a property. It is called "X-IMPORTANCE" (the names of experimental properties must begin with "X-").
BEGIN:VCALENDAR
PRODID:-//Company//Application//EN
VERSION:2.0
X-IMPORTANCE:1
END:VCALENDAR
To get an experimental property, call the getExperimentalProperty(String)
method. This returns the first property with that name in the form of a RawProperty
object.
ICalendar ical = ...
RawProperty property = ical.getExperimentalProperty("X-IMPORTANCE");
String value = property.getValue();
If there is more than one instance of an experimental property, the getExperimentalProperties(String)
method can be called to retrieve all instances:
ICalendar ical = ...
List<RawProperty> properties = ical.getExperimentalProperties("X-IMPORTANCE");
for (RawProperty property : properties){
String value = property.getValue();
}
To add an experimental property to an iCalendar object, call the addExperimentalProperty(String, String)
method. The method (1) creates a RawProperty
object, (2) adds it to the iCalendar object, and (3) returns the RawProperty
object. Returning the created object allows you to assign parameters to the property, if necessary.
ICalendar ical = ...
RawProperty property = ical.addExperimentalProperty("X-IMPORTANCE", "1");
property.setParameter("X-FOO", "bar");
biweekly supports a plugin system that allows you to marshal and unmarshal experimental properties into Java objects. It requires the creation of a property class and a scribe class. This system can also be used to override the scribes of the standard properties, if need be.
The example below will create property and scribe classes for our "X-IMPORTANCE
" example.
The property class extends ICalProperty
. The property can hold either a text value (like "high" or "low") or a number (like "0" or "10").
public class Importance extends ICalProperty {
private Integer number;
private String text;
public Importance(Integer value) {
this.number = value;
}
public Importance(String text) {
this.text = text;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
this.text = null;
}
public String getText() {
return text;
}
public void setText(String text) {
this.number = null;
this.text = text;
}
//optional
@Override
protected void validate(List<ICalComponent> parentComponents, List<String> warnings) {
if (number == null && text == null) {
warnings.add("Value is null.");
}
if (number != null && number < 0) {
warnings.add("Number cannot be less than 0");
}
}
}
The validate()
method is optional. It validates the contents of the object when ICalendar.validate()
is called. The first parameter, parentComponents
, contains the hierarchy of components to which the property belongs. For example, if the property is inside of a VEVENT
component, which is inside of an VCALENDAR
component, index 0 of the list would be an ICalendar
object and index 1 of the list would be an VEvent
object (the first element of the list will always be an ICalendar
object). The validation warnings are added to the warnings
list.
The scribe class is responsible for reading/writing the property to/from the actual data stream (such as an ".ics" file). It extends the ICalPropertyScribe
class.
public class ImportanceScribe extends ICalPropertyScribe<Importance> {
public ImportanceScribe() {
super(Importance.class, "X-IMPORTANCE", ICalDataType.INTEGER);
}
//optional
//determines the iCal data type of the property's value
@Override
protected ICalDataType _dataType(Importance property) {
if (property.getText() != null) {
return ICalDataType.TEXT;
}
return ICalDataType.INTEGER;
}
//optional
//tweaks the parameters before the property is written
@Override
protected void _prepareParameters(Importance property, ICalParameters copy) {
Integer value = property.getNumber();
if (value != null && value >= 10) {
copy.put("X-MESSAGE", "very important!!");
}
}
//required
//writes the property to a plain-text iCal
@Override
protected String _writeText(Importance property) {
return write(property);
}
//required
//parses the property from a plain-text iCal
@Override
protected Importance _parseText(String value, ICalDataType dataType, ICalParameters parameters, List<String> warnings) {
value = unescape(value);
return parse(value, dataType);
}
//optional
//writes the property to an XML document (xCal)
@Override
protected void _writeXml(Importance property, XCalElement element) {
Integer value = property.getNumber();
if (value != null) {
if (value > 100) {
throw new SkipMeException("Way too high.");
}
element.append(ICalDataType.INTEGER, value.toString()); //writes: <x-importance><integer>1</integer></x-importance>
return;
}
String text = property.getText();
if (text != null) {
element.append(ICalDataType.TEXT, text); //writes: <x-importance><text>high</text></x-importance>
}
}
//optional
//reads the property from an XML document (xCal)
@Override
protected Importance _parseXml(XCalElement element, ICalParameters parameters, List<String> warnings) {
String text = element.first(ICalDataType.TEXT);
if (text != null) {
return new Importance(text);
}
String number = element.first(ICalDataType.INTEGER);
if (number != null) {
try {
return new Importance(Integer.valueOf(number));
} catch (NumberFormatException e) {
throw new CannotParseException("Numeric value expected: " + number);
}
}
return new Importance(0);
}
//optional
//writes the property to a JSON document (jCal)
@Override
protected JCalValue _writeJson(Importance property) {
return JCalValue.single(write(property));
}
//optional
//reads the property from a JSON document (jCal)
@Override
protected Importance _parseJson(JCalValue value, ICalDataType dataType, ICalParameters parameters, List<String> warnings) {
String valueStr = value.asSingle();
return parse(valueStr, dataType);
}
private Importance parse(String value, ICalDataType dataType) {
if (dataType == ICalDataType.TEXT) {
return new Importance(value);
}
try {
return new Importance(Integer.valueOf(value));
} catch (NumberFormatException e) {
throw new CannotParseException("Numeric value expected: " + value);
}
}
private String write(Importance property) {
String text = property.getText();
if (text != null) {
return text;
}
Integer number = property.getNumber();
if (number != null) {
return number.toString();
}
return "";
}
}
The property class, the name of the property, and the property's default data type are passed into the parent constructor.
The _prepareParameters()
method allows you to modify the property's parameters before the property is written. The second argument of the method is a copy of the property's parameters, and is the object to which you should make the modifications. A copy is made so that the property object itself does not get modified every time it is written.
The _writeText()
method marshals the property's value to a string for a plain-text iCalendar object.
A SkipMeException
can be thrown if it is determined that the property should NOT be written out to the data stream.
The _parseText()
method is responsible for creating the Java object, based on what is read from a plain-text iCalendar data stream.
A SkipMeException
can be thrown if it is determined that the property should NOT be added to the ICalendar
Java object. The exception message will be logged as a warning. Warnings can be retrieved using the ICalReader.getWarnings()
method.
A CannotParseException
can be thrown if the property value cannot be parsed and a Java object cannot be created. When this happens, the property is instead unmarshalled as an experimental property, which means that its raw value can still be retrieved by calling the getExperimentalProperty(String)
method on the component object that the property belongs to. The exception message will be logged as a warning. Warnings can be retrieved using the ICalReader.getWarnings()
method.
The _writeXml()
method marshals the property's value to an XML document (xCal standard). The XCalElement
object is the element which contains the iCalendar property. It wraps a org.w3c.dom.Element
object, adding xCal-specific functionality. The raw org.w3c.dom.Element
object can be retrieved by calling XCalElement.getElement()
.
A SkipMeException
can be thrown if it is determined that the property should NOT be written out to the data stream.
The _parseXml()
method is responsible for creating the Java object, based on what is read from the XML document (xCal standard). The XCalElement
object is the element which contains the iCalendar property. It wraps a org.w3c.dom.Element
object, adding xCal-specific functionality. The raw org.w3c.dom.Element
object can be retrieved by calling XCalElement.getElement()
.
The property's parameters are parsed before _parseXml()
is called. They can be accessed from the ICalParameters
object. Also, note that the XML elements that the parameters are stored in are removed from the XCalElement
object.
A SkipMeException
can be thrown if it is determined that the property should NOT be added to the ICalendar
Java object. The exception message will be logged as a warning. Warnings can be retrieved using the ICalReader.getWarnings()
method.
A CannotParseException
can be thrown if the property value cannot be parsed and a Java object cannot be created. When this happens, the property is instead unmarshalled as an experimental property, which means that its raw value can still be retrieved by calling the getExperimentalProperty(String)
method on the component object that the property belongs to. The exception message will be logged as a warning. Warnings can be retrieved using the ICalReader.getWarnings()
method.
The _writeJson()
method marshals the property's value to a JSON document (jCal standard). The method returns a JCalValue
object, which represents the property's value and data type in JSON form.
The class has factory methods to aid in the construction the three most typical types of jCal values.
- single - Defines the value as a single value, such as a single string. Most properties are single valued.
- multi - Defines the value as a list of values (such as the EXDATE property).
- structured - Defines the value as a structured value (i.e. a "list of lists", such as the REQUEST-STATUS property).
Objects may be passed into these methods. Primitive wrapper objects, such as Integer
and Boolean
, will be converted to their appropriate JSON data type. All other objects will be passed into the JSON stream as strings (their toString()
method will be invoked). Null values will be converted to empty strings.
Examples:
JCalValue value = JCalValue.single("one");
//yields: ["propName", {}, "data-type", "one"]
JCalValue value = JCalValue.multi("one", 2, true);
//yields: ["propName", {}, "data-type", "one", 2, true]
JCalValue value = JCalValue.structured(1, Arrays.asList(2, 3), 4);
//yields: ["propName", {}, "data-type", [1, [2, 3], 4]]
A SkipMeException
can be thrown if it is determined that the property should NOT be written out to the data stream.
The _parseXml()
method is responsible for creating the Java object, based on what is read from the XML document (xCal standard). The XCalElement
object is the element which contains the iCalendar property. It wraps a org.w3c.dom.Element
object, adding xCal-specific functionality. The raw org.w3c.dom.Element
object can be retrieved by calling XCalElement.getElement()
.
The _parseJson()
method is responsible for creating the Java object, based on what is read from a JSON document (jCal standard). The JSON data is stored in the JCalValue
object.
JCalValue
has helper methods to aid in the retrieval the three most typical types of jCal values.
- asSingle - Gets the value of a property that contains a single value. Most properties are single valued.
- asMulti - Gets the value of a property that contains multiple values (such as the EXDATE property).
- asStructured - Gets the value of a property that contains a structured value (i.e. a "list of lists", such as the REQUEST-STATUS property).
The property's parameters are parsed before _parseJson()
is called. They can be accessed from the ICalParameters
object.
A SkipMeException
can be thrown if it is determined that the property should NOT be added to the ICalendar
Java object. The exception message will be logged as a warning. Warnings can be retrieved using the JCalReader.getWarnings()
method.
A CannotParseException
can be thrown if the property value cannot be parsed and a Java object cannot be created. When this happens, the property is instead unmarshalled as an experimental property, which means that its raw value can still be retrieved by calling the getExperimentalProperty(String)
method on the component object that the property belongs to. The exception message will be logged as a warning. Warnings can be retrieved using the JCalReader.getWarnings()
method.
Before an iCalendar data stream is parsed, the scribe class must be registered with the reader object. Then, once an ICalendar
object has been read, the instances of the property can be retrieved by calling the getProperty(Class)
method.
String icalStr =
"BEGIN:VCALENDAR\r\n" +
"PRODID:-//Company//Application//EN\r\n" +
"VERSION:2.0\r\n" +
"X-IMPORTANCE:1\r\n" +
"END:VCALENDAR\r\n";
//using "Biweekly" class
ICalendar ical = Biweekly.parse(icalStr)
.register(new ImportanceScribe())
.first();
Importance property = ical.getProperty(Importance.class);
//using "ICalReader" class
ICalReader icalReader = new ICalReader(icalStr);
icalReader.registerScribe(new ImportanceScribe());
ICalendar ical = icalReader.readNext();
Importance property = ical.getProperty(Importance.class);
To add instances of the property to the iCalendar object, call the addProperty()
method on the component that the property belongs to. Then, register the scribe class with the writer object and perform the write operation.
ICalendar ical = new ICalendar();
Importance property = new Importance(1);
ical.addProperty(property);
//using "Biweekly" class
String icalStr = Biweekly.write(ical)
.register(new ImportanceScribe())
.go();
//using "ICalWriter" class
StringWriter sw = new StringWriter();
ICalWriter icalWriter = new ICalWriter(sw);
icalWriter.registerScribe(new ImportanceScribe());
icalWriter.write(ical);
String icalStr = sw.toString();
biweekly is maintained by Michael Angstadt
Table of Contents
Getting started
Examples
FAQ
Javadocs
Downloads
1 An Overview of the iCalendar data format
2 Reading and Writing iCalendar data with biweekly
2.1 Plain-text (traditional)
2.2 XML-encoded (xCal)
2.3 JSON-encoded (jCal)
4 Working with Timezones
4.1 0.4.6 and earlier
4.2 0.5.0 and later
5 Dealing with Non-standard Data
5.1 Non-standard components
5.2 Non-standard properties
5.3 Non-standard parameters
6 Project Information
6.1 Dependencies
6.2 Supported Specifications
6.3 Changelog
7 Reference
7.1 iCalendar Component Reference
7.2 iCalendar Property Reference
7.3 Javadocs