-
Notifications
You must be signed in to change notification settings - Fork 92
Property scribe
ez-vcard contains a pluggable API for creating your own property classes. This can be useful if there is an extended property that your application makes frequent use of.
To make use of this API, you will need to create a property class and a property scribe class. Then, when reading or writing your vCard, you must register the property scribe with the reader/writer class's registerScribe
method.
Note that this is the same framework that ez-vcard uses to implement all of the standard vCard properties. So, feel free to explore the ezvcard.property
and ezvcard.io.scribe
packages in the source code for more examples.
The property class holds the unmarshalled value of the property. This class must extend the VCardProperty
class.
The example below shows a property class for the X-MS-ANNIVERSARY
property that Microsoft Outlook often adds to its vCards.
public class MSAnniversary extends VCardProperty {
private Date date;
public MSAnniversary(Date date){
this.date = date;
}
public MSAnniversary(MSAnniversary original){
date = (original.date == null) ? null : new Date(original.date.getTime());
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
@Override
protected void _validate(List<Warning> warnings, VCardVersion version, VCard vcard) {
if (date == null) {
warnings.add(new Warning("No date is defined."));
}
if (date != null && date.after(new Date())) {
warnings.add(new Warning("Anniversary date is in the future."));
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((date == null) ? 0 : date.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!super.equals(obj)) return false;
if (getClass() != obj.getClass()) return false;
MSAnniversary other = (MSAnniversary) obj;
if (date == null) {
if (other.date != null) return false;
} else if (!date.equals(other.date)) return false;
return true;
}
@Override
public MSAnniversary copy(){
return new MSAnniversary(this);
}
}
Optionally, you may define a _validate
method, as shown above. This method is used to verify the correctness of a property's data model. It is invoked when the VCard.validate()
method is called.
Parameter | Description |
---|---|
List<Warning> warnings |
The list that all validation warnings should be added to. |
VCardVersion version |
The vCard version that the vCard is being validated under. |
VCard vcard |
The vCard that is being validated. |
In order to support the VCard
class's copy constructor, the property class MUST have EITHER a copy
method OR a copy constructor, but PREFERRABLY both.
The property class SHOULD implement the hashCode()
and equals()
methods in order for the VCard.equals()
method to work properly. Each of these methods MUST call their super method because the parent VCardProperty
class has fields of its own.
The scribe class is responsible for reading and writing the property to a file or other data stream. Using more common terminology, you could also refer to this class as a "marshaller" or "serializer". Scribe classes must extend the VCardPropertyScribe
class and implement a number of methods.
The example below shows a scribe class for our MSAnniversary
property.
public class MSAnniversaryScribe extends VCardPropertyScribe<MSAnniversary> {
public MSAnniversaryScribe() {
super(MSAnniversary.class, "X-MS-ANNIVERSARY");
}
//required
//defines the property's default data type
@Override
protected VCardDataType _defaultDataType(VCardVersion version) {
return VCardDataType.DATE;
}
//optional
//determines the data type based on the property value
@Override
protected VCardDataType _dataType(MSAnniversary property, VCardVersion version) {
return VCardDataType.DATE;
}
//optional
//tweaks the property's parameters before the property is written
@Override
protected void _prepareParameters(MSAnniversary property, VCardParameters copy, VCardVersion version, VCard vcard) {
//empty
}
//required
//writes the property's value to a plain-text vCard
@Override
protected String _writeText(MSAnniversary property, VCardVersion version) {
return escape(write(property.getDate()));
}
//required
//parses the property's value from a plain-text vCard
@Override
protected MSAnniversary _parseText(String value, VCardDataType dataType, VCardVersion version, VCardParameters parameters, List<String> warnings) {
Date date = parse(unescape(value));
return new MSAnniversary(date);
}
//optional
//writes the property to an XML document (xCard)
@Override
protected void _writeXml(MSAnniversary property, XCardElement element) {
Date date = property.getDate();
if (date != null){
element.append(VCardDataType.DATE, write(date));
}
}
//optional
//parses the property from an XML document (xCard)
@Override
protected MSAnniversary _parseXml(XCardElement element, VCardParameters parameters, List<String> warnings) {
String dateStr = element.first(VCardDataType.DATE);
if (dateStr == null) {
throw new CannotParseException("No <date> elements found.");
}
Date date = parse(dateStr);
return new MSAnniversary(date);
}
//optional
//parses the property value from an HTML page (hCard)
@Override
protected MSAnniversary _parseHtml(HCardElement element, List<String> warnings) {
String dateStr = element.value();
Date date = parse(dateStr);
return new MSAnniversary(date);
}
//optional
//writes the property to a JSON stream (jCard)
@Override
protected JCardValue _writeJson(MSAnniversary property) {
Date date = property.getDate();
String dateStr = (date == null) ? "" : write(date);
return JCardValue.single(dateStr);
}
//optional
//parses the property value from a JSON stream (jCard)
@Override
protected MSAnniversary _parseJson(JCardValue value, VCardDataType dataType, VCardParameters parameters, List<String> warnings) {
String dateStr = value.asSingle();
Date date = parse(dateStr);
return new MSAnniversary(date);
}
private String write(Date date){
if (date == null) return "";
DateFormat df = new SimpleDateFormat("yyyyMMdd");
return df.format(date);
}
private Date parse(String dateStr){
if (dateStr == null) return null;
DateFormat df = new SimpleDateFormat("yyyyMMdd");
try {
return df.parse(dateStr);
} catch (ParseException e){
throw new CannotParseException("Date format is invalid.");
}
}
}
The constructor calls the parent class constructor, passing in the property class and the property name.
Required. Some properties can hold different types of data. This method returns the property's default data type. If a property instance's data type is different than the default data type, then a VALUE
parameter will be added to the property when it is written to a plain-text vCard. If a property instance's data type is the same as the default data type, then a VALUE
parameter will not be added to the property.
Optional. Determines a property instance's data type. If this differs from the default data type, then a VALUE
parameter will be added to the property when written to a plain-text vCard.
Method argument | Description |
---|---|
MSAnniversary property |
The property object that is being written. |
VCardVersion version |
The version of the vCard that is being generated. |
Optional. Allows the property's parameters to be tweaked before the property is written. A copy of the property's parameters is passed into this method so that the data in the original property object will not be modified. Our property does not use parameters, so our implementation of this method is empty.
Method argument | Description |
---|---|
MSAnniversary property |
The property object that is being written. |
VCardParameters copy |
A copy of the property's parameters. All modifications must be made to this copy. |
VCardVersion version |
The version of the vCard that is being generated. |
VCard vcard |
The vCard that is being written. |
Required. Generates the plain-text representation of the property.
Method argument | Description |
---|---|
MSAnniversary property |
The property object that is being written. |
VCardVersion version |
The version of the vCard that is being generated. |
Special characters: It is recommended that all special vCard characters be backslash-escaped when written to a plain-text vCard if they do not have special meanings within the property value. These special characters are: comma (,
), semicolon (;
), and backslash (\
). The VCardPropertyScribe.escape()
method can be used to do this. Newline escaping and line folding do not need to be handled here because they are handled in the VCardWriter
class.
Our MSAnniversaryScribe
class will produce a vCard property that looks something like this:
X-MS-ANNIVERSARY:20110301
Required. Parses the plain-text representation of the property.
Method argument | Description |
---|---|
String value |
The property value, as read off the wire. |
VCardDataType dataType |
The property's data type. This will be either the value of the VALUE parameter, or the property's default data type if no VALUE parameter is present). |
VCardVersion version |
The version of the vCard that is being parsed. |
VCardParameters parameters |
The property's parameters. These parameters will be assigned to the property object when the _parseText method returns. |
List<String> warnings |
Any non-critical parsing problems can be added to this list. |
Special characters: It is recommended that you account for backslash-escaped characters when parsing a property value from a plain-text vCard. Characters that may be escaped include: comma (,
), semicolon (;
), and backslash (\
). The VCardPropertyScribe.unescape()
method can be used to do this. Newline unescaping and line unfolding do not need to be handled here because they are handled in the VCardReader
class.
Optional. Generates the XML (xCard) representation of the property.
Method argument | Description |
---|---|
MSAnniversary property |
The property object that is being written. |
XCardElement element |
Represents the property's XML element. This class wraps xCard functionality around a raw org.w3c.dom.Element object. This object can be retrieved by calling XCardElement.element() , and can be modified as needed. |
In our example, the XCardElement.append()
method is used to add the xCard <date>
element to the property's XML element.
Our MSAnniversaryScribe
class will produce an XML element that looks something like this:
<x-ms-anniversary xmlns="urn:ietf:params:xml:ns:vcard-4.0">
<date>20110301</date>
</x-ms-anniversary>
Optional. Parses the XML (xCard) representation of the property.
Method argument | Description |
---|---|
XCardElement element |
Represents the property's XML element. This class wraps xCard functionality around a raw org.w3c.dom.Element object. This object can be retrieved by calling XCardElement.element() . |
VCardParameters parameters |
The property's parameters. These parameters will be assigned to the property object when the _parseXml method returns. |
List<String> warnings |
Any non-critical parsing problems can be added to this list. |
Optional. Parses the HTML (hCard) representation of the property.
Method argument | Description |
---|---|
HCardElement element |
Represents the property's HTML element. |
List<String> warnings |
Any non-critical parsing problems can be added to this list. |
Optional. Generates the JSON (jCard) representation of the property.
Method argument | Description |
---|---|
MSAnniversary property |
The property object that is being written. |
Our example uses the JCardValue.single()
factory method to create a new JCardValue
instance which contains a single value.
The JCardValue
class contains the following factory methods to aid in the construction the three most typical types of jCard 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. -
structured
- Defines the value as a structured value (i.e. a "list of lists", such as theN
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.
JCardValue value = JCardValue.single("one");
//yields: ["propName", {}, "text", "one"]
JCardValue value = JCardValue.multi("one", 2, true);
//yields: ["propName", {}, "text", "one", 2, true]
JCardValue value = JCardValue.structured(1, Arrays.asList(2, 3), 4);
//yields: ["propName", {}, "integer", [1, [2, 3], 4]]
Our MSAnniversary
class will produce a jCard property that looks something like this:
["x-ms-anniversary", {}, "date", "20110301"]
Optional. Parses the JSON (jCard) representation of the property.
Method argument | Description |
---|---|
JCardValue value |
Represents the property's jCard value. |
VCardDataType dataType |
The property's data type. |
VCardParameters parameters |
The property's parameters. These parameters will be assigned to the property object when the _parseJson method returns. |
List<Warning> warnings |
Any non-critical parsing problems can be added to this list. |
In our example, the JCardValue.asSingle()
method is called to convert the raw JSON value to a single String value.
The JCardValue
class has helper methods to aid in the retrieval of the three most typical types of jCard 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. -
asStructured
- Gets the value of a property that contains a structured value (i.e. a "list of lists", such as theN
property).
SkipMeException
: Can be thrown from any of the parse
or write
methods if it is determined that the property should NOT be added to the VCard
object (in the case of the parse
methods) or should NOT be written to the data stream (in the case of the write
methods). When this exception is thrown from a parse
method, the exception message will be logged as a warning.
CannotParseException
: Can be thrown from the parse
methods if the property value cannot be unmarshalled. In our example, we throw this when the date string cannot be parsed into a Date
object. This will cause the property to be unmarshalled as a RawProperty
instead. RawProperties
can be retrieved by calling the VCard.getExtendedProperty()
method. This will also cause a warning will be added to the parser's warnings list.
Before a vCard is parsed, the property scribe must be registered with the reader object. Then, once a vCard has been read, the instances of the property can be retrieved using the VCard.getProperty(Class)
or VCard.getProperties(Class)
methods.
//using the Ezvcard class
Reader reader = ...
VCard vcard = Ezvcard.parse(reader).register(new MSAnniversaryScribe()).first();
reader.close();
MSAnniversary first = vcard.getProperty(MSAnniversary.class);
List<MSAnniversary> all = vcard.getProperties(MSAnniversary.class);
//using a reader class
Reader reader = ...
VCardReader vcr = new VCardReader(reader);
vcr.registerScribe(new MSAnniversaryScribe());
VCard vcard = vcr.readNext();
reader.close();
MSAnniversary first = vcard.getProperty(MSAnniversary.class);
List<MSAnniversary> all = vcard.getProperties(MSAnniversary.class);
To add an instance of a your property class to a VCard
object, call the VCard.addProperty(VCardProperty)
method. Then, register the property's scribe with the writer class before writing the vCard.
VCard vcard = new VCard();
MSAnniversary anniversary = new MSAnniversary(...);
vcard.addProperty(anniversary);
Writer writer = ...
//using the Ezvcard class
Ezvcard.write(vcard).register(new MSAnniversaryScribe()).go(writer);
writer.close();
//using writer class
VCardWriter vcw = new VCardWriter(writer);
vcw.registerScribe(new MSAnniversaryScribe());
vcw.write(vcard);
vcw.close();
ez-vcard is maintained by Michael Angstadt
Table of Contents
Getting started
Examples
FAQ
Javadocs
Downloads
1 An Overview of the vCard data format
2 Reading and Writing vCard data with ez-vcard
2.1 Plain-text (traditional)
2.2 XML-encoded (xCard)
2.3 JSON-encoded (jCard)
2.4 HTML-encoded (hCard)
3 Differences between the vCard versions
4 Dealing with Non-standard Data
4.1 Working with non-standard properties and parameters
4.2 Property scribe
5 Project Information
5.1 News
5.2 Dependencies
5.3 Supported Specifications
5.4 Changelog
6 Reference
6.1 vCard Property Reference
6.2 Javadocs