Java tools for JSContact RFC9553 creation, validation, serialization/deserialization and conversion from and to vCard 4.0 RFC6350, xCard RFC6351 and jCard RFC7095. Conversion from JSContact to vCard formats and viceversa is based on the rules described in RFC9555 Validation and conversion of vCard formats leverage the features provided by ez-vcard Java library.
<dependency>
<groupId>it.cnr.iit.jscontact</groupId>
<artifactId>jscontact-tools</artifactId>
<version>1.0.1</version>
</dependency>
compile 'it.cnr.iit.jscontact:jscontact-tools:1.0.1'
- Creation
- Validation
- Serialization/Deserialization
- Localization
- vCard Conversion
- JSContact Conversion
- Using JSContact in RDAP
- Testing
- ez-vcard extensions
- ez-vcard bugs
- JSContact Compliance
- References
Object creation is achieved through builders. In addition to the class builders, the following collections builders are provided:
- ContextsBuilder
- AddressContextsBuilder
- PhoneFeaturesBuilder
- NameComponentsBuilder
- AddressComponentsBuilder
Simplest maps can be loaded by putting entries singularly. Here in the following a successful creation of an EmailAddress instance is shown.
EmailAddress email = EmailAddress.builder()
.address("[email protected]")
.contexts(ContextsBuilder.builder().work().private_().build())
.build();
The build method throws the java.lang.NullPointerException
when a required property is missing.
Here in the following an unsuccessful creation of an EmailAddress
instance is shown.
// address is missing in EmailAddress
EmailAddress.builder().contexts(ContextsBuilder.builder().work().build()).build();
Creation can be achieved through cloning as well. Cloning can be only applied to Card.
Here in the following a test assessing a successful creation of a cloned Card instance.
@Test
public void testClone1() throws IOException {
String json = IOUtils.toString(Objects.requireNonNull(getClass().getClassLoader().getResourceAsStream("jcard/jsCard-Multilingual.json")), StandardCharsets.UTF_8);
Card jsCard = Card.toCard(json);
assertTrue("testClone1", jsCard.equals(jsCard.clone()));
}
Even if a JSContact Card is correctly created by builder, it might need to be validated as it were obtained from an external producer through deserialization.
Validation is performed on JSContact Card by invoking the method isValid
.
This method returns a boolean value: true
if the object satisfies all the constraints included in [RFC9553], false
otherwise.
If the validation process doesn't end successfully, the list of error messages can be obtained by calling the getValidationMessages
method.
Here in the following a method testing an unsuccessfully ended validation is shown.
@Test
public void testInvalidCountryCode() {
// a contry code must be two-character long
Map addresses = new HashMap<String,Address>() {{ put("ADR-1", Address.builder()
.countryCode("ita")
.build());
}};
Card jsCard = Card.builder()
.uid(getUUID())
.addresses(addresses)
.build();
assertTrue("testInvalidCountryCode-1", !jsCard.isValid());
assertTrue("testInvalidCountryCode-2", jsCard.getValidationMessage().equals("invalid countryCode in Address"));
}
Validation of all vCard formats is supported as well. At present, the following validation methods are available:
- JCardValidator
- void validate(String json)
- void validate(JsonNode jsonNode)
- VCardValidator
- void validate(String vcf)
- XCardValidator
- void validate(String xml)
All the methods can raise a CardException
.
JsonNode
represents the root node in Jackson library (com.fasterxml.jackson.databind.JsonNode
).
With regard to UTCDateTime type validation, values including trailing zeros in fractional second values are not signaled as invalid but automatically converted in values not including milliseconds (e.g. "2010-10-10T10:10:10.000Z" is encoded and serialized as "2010-10-10T10:10:10Z")
JSContact Card serialization/deserializaion is performed through Jackson library annotations.
Card jsCard = Card.builder.build();
String serialized = mapper.writeValueAsString(jsCard);
To pretty print serialized Card objects, use the following:
Card jsCard = Card.builder.build();
String serialized = PrettyPrintSerializer.print(jsCard);
String json = "{"uid": \"urn:uuid:c642b718-7c89-49f4-9497-d9fb279bb437\"}";
ObjectMapper mapper = new ObjectMapper();
Card jsCard = mapper.readValue(json, Card.class);
Localizations in JSContact are implemented through the localizations
map in the Card object.
At present, the following methods support localizations handling:
-
void addLocalization(String language, String path, JsonNode object)
This method allows for adding a new localization to the
localizations
map. Thepath
parameter is the JSON pointer RFC6901 to the property being localized. Theobject
parameter represents the JSON object in Jackson library. -
Map<String,JsonNode> getLocalizationsPerPath(String path)
This method returns all the localizations for a given path. The keys of the returned map are the language tags of the related localizations.
-
Map<String,JsonNode> getLocalizationsPerLanguage(String language)
This method returns all the localizations for a given language. The keys of the returned map are the paths of the related localizations.
-
JsonNode getLocalization(String language, String path)
This method returns a localization object for a given <language,path> pair.
At present, the following converting methods are available:
- EZVCard2JSContact
- List convert(VCard... vcard)
- VCard2JSContact
- List convert(String vcf)
- JCard2JSContact
- List convert(String json)
- List convert(JsonNode jsonNode)
- XCard2JSContact
- List convert(String xml)
All the methods return a list of JSContact Card objects and can raise a CardException
.
VCard
is the class mapping a vCard in ez-vcard Java library.
JsonNode
represents the root node in Jackson library (com.fasterxml.jackson.databind.JsonNode
).
The conversion is executed according to the following rules:
-
The conversion is based on the content of the JSContact RFCs.
-
The card components (i.e. properties, parameters or values) considered in the Other RFCs as well as the additonal components defined in RFC9554 are matched.
-
An unmatched property is converted into an entry of the topmost Card
vCardProps
map. The following unmatched properties are considered:
CLIENTPIDMAP VERSION XML -
An unmatched parameter is converted into an entry of a
vCardParams
map. The following unmatched parameters are considered: PID GROUP -
Validation is performed before conversion if the
validateCard
configuration property is set totrue
. -
Default values for the configuration properties are:
customTimeZonesPrefix = "tz"
validateCard = true
usePropIds = true
setAutoFullAddress = true
setAutoMediaType = true
convertGenderToSpeakToAs = true
-
The sex information of the GENDER property can be mapped to the SpeakToAs object if GRAMGENDER is missing and if the
convertGenderToSpeakToAs
configuration value is set to true as in the following:GENDER SpeakToAs.grammaticalGender
M masculine F feminine O animate N common U SpeakToAs = null -
Where a language is required to represent a localization and the language is not specified,
en
is used by default to set thedefaultLanguage
mapping configuration parameter. -
Regardless of their positions inside the vCard, properties mapped as Anniversary objects appear in the following order:
- BDAY (BIRTHDATE)
- DEATHDAY (DEATHDATE)
- ANNIVERSARY
-
Regardless of their positions inside the vCard, properties mapped as PersonalInfo objects appear in the following order:
- HOBBY
- INTEREST
- EXPERTISE
-
Regardless of their positions inside the vCard, properties mapped as Media objects appear in the following order:
- PHOTO
- SOUND
- LOGO
-
Regardless of their positions inside the vCard, properties mapped as Calendar objects appear in the following order:
- CALURI
- FBURL
-
Regardless of their positions inside the vCard, properties mapped as Link objects appear in the following order:
- URL
- CONTACT-URI
-
Regardless of their positions inside the vCard, properties mapped as Directory objects appear in the following order:
- SOURCE
- ORG-DIRECTORY
-
Regardless of their positions inside the vCard, properties mapped as Title objects appear in the following order:
- TITLE
- ROLE
-
If an ADR element doesn't include the LABEL parameter, based on the value of
setAutoFullAddress
mapping configuration parameter , the full address results from the newline-delimited concatenation of the non-empty address components. -
Categories appear in the "keywords" map according to the values of the PREF parameter of the CATEGORIES properties.
-
Members appear in the "members" map according to the values of the PREF parameter of the MEMBER properties.
-
JSContact UTCDateTime type is mapped to Java Calendar.
-
Media type information of
Media
objects is automatically detected when the MEDIATYPE parameter is missing. -
A custom time zone (i.e. a time zone including non-zero minutes or non-IANA time zone) is transformed into a
customTimeZones
map entry whose key is prefixed thecustomTimeZonesPrefix
configuration property concatenated with an incremental positive integer (e.g. "\tz1") -
The TZ and GEO properties can be associated to an ADR property by grouping them together through the group construct.
-
Either the "ISO-3166-1-alpha-2" parameter (that maybe used in RDAP) is converted.
-
FN property with DERIVED parameter set to true is not converted if a corresponding N property exists.
By default, where a collection of objects is mapped to a map of <key,object> entries, the key has the following format: + "-" + <index of the element among the vCard sibling elements (starting from 1)> (e.g. "ADR-1") This setting schema can be modified by defining a different one assigning key values based on the positions of vCard elements. To do that, the following steps must be followed:
-
set the
usePropIds
property of theVCard2JSContactConfig
object tofalse
-
create a
VCard2JSContactIdsProfile
object and assign theidsProfileToUse
ofVCard2JSContactConfig
object property with it
A pre-defined conversion profile to convert a jCard instance inside an RDAP response RFC9083 is available. The values of the map keys used in such profile are defined in draft-ietf-regext-rdap-jscontact. Additional setting rules are shown in the class RdapJSContactIdsProfile.
At present, the following converting methods are available:
- JSContact2EZVCard
- List convert(Card... jsContacts)
- List convert(String json)
- JSContact2VCard
- String convertToText(Card... jsContact)
- JSContact2JCard
- String convertToJson(Card... jsContact)
- JsonNode convertToJsonNode(Card... jsContact)
- JSContact2XCard
- String convertToXml(Card... jsContact)
All the methods take in input a list of JSContact Card objects and can raise the CardException
exception.
VCard
is the class mapping a vCard in ez-vcard Java library.
JsonNode
represents the root node in Jackson library (com.fasterxml.jackson.databind.JsonNode
).
-
The conversion is based on the content of the JSContact RFCs.
-
An entry of the topmost Card/CardGroup
vCardProps
map is converted into the related vCard property . The following properties are considered: CLIENTPIDMAP XML -
An entry of an object
vCardParams
map is converted into a vCard parameter. The following parameters are considered: PID -
Default values for the configuration properties are:
validateCard = true
setAutoAddrLabel = true
setPropIdParam = true
convertTimezoneToOffset = true
-
The "timeZone" property can be mapped to either a TZ parameter or the TZ property either preserving the time zone name or the time zone offset extracted from the
customTimeZones
map. Time zone names in the format "Etc/GMT(+|-).." can be mapped to offsets based on the value of mapping configuration parameterconvertTimezoneToOffset
-
If the "name.full" property is missing, the FN value is generated starting from the "name" property. The name components are separated by the "separator" value if present, space otherwise. If the "name" property is missing as well, the FN value is set to the "uid" property.
-
The "street" component of ADR property results from the concatenation of "district", "block", "name", "number" and "direction" non-empty values presented in the "street" member of the "Address" object. Such values are separated by the "defaultSeparator"/"separator" value if present, comma otherwise.
-
The "extension" component of ADR property results from the concatenation of "building", "floor", "apartment", "room", "landmark" and "extention" non-empty values presented in the "components" member of the "Address" object. Such values are separated by the "defaultSeparator"/"separator" value if present, comma otherwise.
-
The LABEL parameter of the ADR property is equal to the "full" property of the "Address" object. If the full address is missing, based on the value of mapping configuration parameter
setAutoAddrLabel
, the value of the LABEL parameter can result from the newline-delimited concatenation of the non-empty "Address" members or. -
The "PROP-ID" parameter can be mapped to the value of a map key based on the value of the
setPropIdParam
mapping configuration parameter. -
The "countryCode" member of the Address type always converts to the vCard CC parameter.
Here in the following two examples of conversion between vCard and JSContact Card object.
@Test
public void testAddresses4() throws IOException, CardException {
String vcard = "BEGIN:VCARD\n" +
"VERSION:4.0\n" +
"FN:test\n" +
"GROUP1.ADR;CC=US:;;54321 Oak St;Reston;VA;20190;USA\n" +
"GROUP1.GEO:geo:46.772673,-71.282945\n" +
"END:VCARD";
Card jsCard = vCard2JSContact.convert(vcard).get(0);
assertNotNull("testAddresses4 - 1", jsCard.getAddresses());
assertEquals("testAddresses4 - 2", 1, jsCard.getAddresses().size());
assertEquals("testAddresses4 - 3", "US", jsCard.getAddresses().get("ADR-1").getCountryCode());
assertEquals("testAddresses4 - 4", "USA", jsCard.getAddresses().get("ADR-1").getCountry());
assertEquals("testAddresses4 - 5", "20190", jsCard.getAddresses().get("ADR-1").getPostcode());
assertEquals("testAddresses4 - 6", "Reston", jsCard.getAddresses().get("ADR-1").getLocality());
assertEquals("testAddresses4 - 7", "VA", jsCard.getAddresses().get("ADR-1").getRegion());
assertEquals("testAddresses4 - 8", "54321 Oak St", jsCard.getAddresses().get("ADR-1").getStreetDetails());
assertEquals("testAddresses4 - 9", "54321 Oak St\nReston\nVA\n20190\nUSA", jsCard.getAddresses().get("ADR-1").getFullAddress());
assertEquals("testAddresses4 - 10", "geo:46.772673,-71.282945", jsCard.getAddresses().get("ADR-1").getCoordinates());
}
@Test
public void testJCardGroup1() throws IOException, CardException {
String jcard="[" +
"[\"vcard\", [ " +
"[\"version\", {}, \"text\", \"4.0\"], " +
"[\"kind\", {}, \"text\", \"group\"], " +
"[\"fn\", {}, \"text\", \"The Doe family\"], " +
"[\"member\", {}, \"uri\", \"urn:uuid:03a0e51f-d1aa-4385-8a53-e29025acd8af\"], " +
"[\"member\", {}, \"uri\", \"urn:uuid:b8767877-b4a1-4c70-9acc-505d3819e519\"] " +
"]] ," +
"[\"vcard\", [ " +
"[\"version\", {}, \"text\", \"4.0\"], " +
"[\"fn\", {}, \"text\", \"John Doe\"], " +
"[\"uid\", {}, \"uri\", \"urn:uuid:03a0e51f-d1aa-4385-8a53-e29025acd8af\"] " +
"]] ," +
"[\"vcard\", [ " +
"[\"version\", {}, \"text\", \"4.0\"], " +
"[\"fn\", {}, \"text\", \"Jane Doe\"], " +
"[\"uid\", {}, \"uri\", \"urn:uuid:b8767877-b4a1-4c70-9acc-505d3819e519\"] " +
"]]" +
"]";
List<Card> jsCards = jCard2JSContact.convert(jcard);
assertEquals("testJCardGroup1 - 1", 3, jsCards.size());
Card jsCardGroup = jsCards.get(0);
assertTrue("testJCardGroup1 - 2", jsCardGroup.getCard().getKind().isGroup());
assertTrue("testJCardGroup1 - 3",StringUtils.isNotEmpty(jsCardGroup.getUid()));
assertEquals("testJCardGroup1 - 4", "The Doe family", jsCardGroup.getCard().getName().getFull());
assertEquals("testJCardGroup1 - 5", 2, jsCardGroup.getMembers().size());
assertSame("testJCardGroup1 - 6", jsCardGroup.getMembers().get("urn:uuid:03a0e51f-d1aa-4385-8a53-e29025acd8af"), Boolean.TRUE);
assertSame("testJCardGroup1 - 7", jsCardGroup.getMembers().get("urn:uuid:b8767877-b4a1-4c70-9acc-505d3819e519"), Boolean.TRUE);
Card jsCard = jsCards.get(1);
assertEquals("testJCardGroup1 - 8", "urn:uuid:03a0e51f-d1aa-4385-8a53-e29025acd8af", jsCard.getUid());
assertEquals("testJCardGroup1 - 9", "John Doe", jsCard.getName().getFull());
jsCard = jsCards.get(2);
assertEquals("testJCardGroup1 - 10", "urn:uuid:b8767877-b4a1-4c70-9acc-505d3819e519", jsCard.getUid());
assertEquals("testJCardGroup1 - 11", "Jane Doe", jsCard.getName().getFull());
}
Here in the following two examples of conversion between JSContact Card and a vCard.
@Test
public void testAddresses4() throws IOException, CardException {
String jscard = "{" +
"\@type\": \"Card\","
"\"uid\":\"urn:uuid:7e0636f5-e48f-4a32-ab96-b57e9c07c7aa\"," +
"\"name\":{\"full\":\"test\"}," +
"\"addresses\":{" +
"\"ADR-1\": {" +
"\"components\":[ " +
"{\"kind\":\"name\",\"value\":\"54321 Oak St\"}," +
"{\"kind\":\"locality\",\"value\":\"Reston\"}," +
"{\"kind\":\"region\",\"value\":\"VA\"}," +
"{\"kind\":\"country\",\"value\":\"USA\"}," +
"{\"kind\":\"postcode\",\"value\":\"20190\"}" +
"]," +
"\"countryCode\":\"US\"," +
"\"coordinates\":\"geo:46.772673,-71.282945\"" +
"}" +
"}" +
"}";
VCard vcard = jsContact2VCard.convert(jscard).get(0);
assertEquals("testAddresses4 - 1", 1, vcard.getAddresses().size());
assertEquals("testAddresses4 - 2", "US", vcard.getAddresses().get(0).getParameter("CC"));
assertEquals("testAddresses4 - 3", "USA", vcard.getAddresses().get(0).getCountry());
assertEquals("testAddresses4 - 4", "20190", vcard.getAddresses().get(0).getPostalCode());
assertEquals("testAddresses4 - 5", "Reston", vcard.getAddresses().get(0).getLocality());
assertEquals("testAddresses4 - 6", "VA", vcard.getAddresses().get(0).getRegion());
assertEquals("testAddresses4 - 7", "54321 Oak St", vcard.getAddresses().get(0).getStreetAddress());
assertEquals("testAddresses4 - 8", "54321 Oak St\nReston\nVA\n20190\nUSA", vcard.getAddresses().get(0).getLabel());
assertEquals("testAddresses4 - 9", vcard.getAddresses().get(0).getGeo(), GeoUri.parse("geo:46.772673,-71.282945"));
assertEquals("testAddresses4 - 10", "ADR-1", vcard.getAddresses().get(0).getParameter(VCardParamEnum.PROP_ID.getValue()));
}
@Test
public void testCardGroup1() throws IOException, CardException {
String jsCards = "[" +
"{" +
"\@type\": \"Card\","
"\"uid\":\"urn:uuid:2feb4102-f15f-4047-b521-190d4acd0d29\"," +
"\"kind\":\"group\"," +
"\"name\":{\"full\":\"The Doe family\"}," +
"\"members\": {" +
"\"urn:uuid:03a0e51f-d1aa-4385-8a53-e29025acd8af\":true," +
"\"urn:uuid:b8767877-b4a1-4c70-9acc-505d3819e519\":true" +
"}" +
"}," +
"{" +
"\@type\": \"Card\","
"\"uid\":\"urn:uuid:03a0e51f-d1aa-4385-8a53-e29025acd8af\"," +
"\"name\":{\"full\":\"John Doe\"}" +
"}," +
"{" +
"\@type\": \"Card\","
"\"uid\":\"urn:uuid:b8767877-b4a1-4c70-9acc-505d3819e519\"," +
"\"name\":{\"full\":\"Jane Doe\"}" +
"}" +
"]";
List<VCard> vcards = jsContact2VCard.convert(jsCards);
assertEquals("testCardGroup1 - 1", 3, vcards.size());
assertTrue("testCardGroup1 - 3", vcards.get(0).getKind().isGroup());
assertTrue("testCardGroup1 - 4",StringUtils.isNotEmpty(vcards.get(0).getUid().getValue()));
assertEquals("testCardGroup1 - 5", "The Doe family", vcards.get(0).getFormattedName().getValue());
assertEquals("testCardGroup1 - 6", 2, vcards.get(0).getMembers().size());
assertEquals("testCardGroup1 - 7", vcards.get(0).getMembers().get(0).getUri().equals("urn:uuid:03a0e51f-d1aa-4385-8a53-e29025acd8af"), Boolean.TRUE);
assertEquals("testCardGroup1 - 8", vcards.get(0).getMembers().get(1).getUri().equals("urn:uuid:b8767877-b4a1-4c70-9acc-505d3819e519"), Boolean.TRUE);
assertEquals("testCardGroup1 - 9", "urn:uuid:03a0e51f-d1aa-4385-8a53-e29025acd8af", vcards.get(1).getUid().getValue());
assertEquals("testCardGroup1 - 10", "John Doe", vcards.get(1).getFormattedName().getValue());
assertEquals("testCardGroup1 - 11", "urn:uuid:b8767877-b4a1-4c70-9acc-505d3819e519", vcards.get(2).getUid().getValue());
assertEquals("testCardGroup1 - 12", "Jane Doe", vcards.get(2).getFormattedName().getValue());
}
VCards can be parsed/written through the methods of the VCardParser/VCardWiter classes which make use of the encoding scheme descirbed in RFC6868.
Using JSContact in RDAP is supported through JSContactForRdapBuilder and JSContactForRdapGetter classes as it is shown in the following example. The uid property is set to a random value by default. My apologizes for the misuse of the Japanese language.
@Test
public void testJSContactForRdapBuilderAndGetter() throws MissingFieldException, CardException {
Card jsCard = JSContactForRdapBuilder.builder()
.name(JSContactNameForRdapBuilder.builder()
.full("Mario Loffredo")
.surname("Loffredo")
.given("Mario")
.build())
.org(".it Registry")
.email("[email protected]")
.voice("+39.0503139811")
.fax("+39.0503139800")
.address(JSContactAddressForRdapBuilder.builder()
.cc("it")
.country("Italy")
.sp("PI")
.city("Pisa")
.pc("56124")
.street("Via Moruzzi, 1")
.build())
.url("https://www.nic.it")
.nameLoc("jp", JSContactNameForRdapBuilder.builder()
.full("マリオ ロフレド")
.surname("ロフレド")
.given("マリオ")
.build())
.orgLoc("jp", ".itレジストリ")
.addrLoc("jp", JSContactAddressForRdapBuilder.builder()
.cc("it")
.country("イタリア")
.sp("PI")
.city("ピサ")
.pc("56124")
.street("モルッツィ通り、1")
.build())
.build();
JSContactForRdapGetter rdapJSContactGetter = JSContactForRdapGetter.of(jsCard);
assertNotNull("testJSContactForRdapBuilderAndGetter - 1", rdapJSContactGetter.uid());
JSContactNameForRdapGetter rdapJSContactNameGetter = JSContactNameForRdapGetter.of(rdapJSContactGetter.name());
assertEquals("testJSContactForRdapBuilderAndGetter - 2", "Mario Loffredo", rdapJSContactNameGetter.full());
assertEquals("testJSContactForRdapBuilderAndGetter - 3", "Loffredo", rdapJSContactNameGetter.surname());
assertEquals("testJSContactForRdapBuilderAndGetter - 4", "Mario", rdapJSContactNameGetter.given());
assertEquals("testJSContactForRdapBuilderAndGetter - 5", ".it Registry", rdapJSContactGetter.org());
assertEquals("testJSContactForRdapBuilderAndGetter - 6", "[email protected]", rdapJSContactGetter.email());
assertEquals("testJSContactForRdapBuilderAndGetter - 7", "+39.0503139811", rdapJSContactGetter.voice());
assertEquals("testJSContactForRdapBuilderAndGetter - 8", "+39.0503139800", rdapJSContactGetter.fax());
assertEquals("testJSContactForRdapBuilderAndGetter - 9", "https://www.nic.it", rdapJSContactGetter.url());
JSContactAddressForRdapGetter rdapJSContactAddressGetter = JSContactAddressForRdapGetter.of(rdapJSContactGetter.address());
assertEquals("testJSContactForRdapBuilderAndGetter - 10", "it", rdapJSContactAddressGetter.cc());
assertEquals("testJSContactForRdapBuilderAndGetter - 11", "Italy", rdapJSContactAddressGetter.country());
assertEquals("testJSContactForRdapBuilderAndGetter - 12", "PI", rdapJSContactAddressGetter.sp());
assertEquals("testJSContactForRdapBuilderAndGetter - 13", "Pisa", rdapJSContactAddressGetter.city());
assertEquals("testJSContactForRdapBuilderAndGetter - 14", "56124", rdapJSContactAddressGetter.pc());
assertEquals("testJSContactForRdapBuilderAndGetter - 15", "Via Moruzzi, 1", rdapJSContactAddressGetter.street());
JSContactNameForRdapGetter rdapJSContactNameLocGetter = JSContactNameForRdapGetter.of(rdapJSContactGetter.nameLoc("jp"));
assertEquals("testJSContactForRdapBuilderAndGetter - 16", "マリオ ロフレド", rdapJSContactNameLocGetter.full());
assertEquals("testJSContactForRdapBuilderAndGetter - 17", "ロフレド", rdapJSContactNameLocGetter.surname());
assertEquals("testJSContactForRdapBuilderAndGetter - 18", "マリオ", rdapJSContactNameLocGetter.given());
assertEquals("testJSContactForRdapBuilderAndGetter - 19", "マリオ", rdapJSContactNameLocGetter.given());
JSContactAddressForRdapGetter rdapJSContactAddressLocGetter = JSContactAddressForRdapGetter.of(rdapJSContactGetter.addressLoc("jp"));
assertEquals("testJSContactForRdapBuilderAndGetter - 20", "it", rdapJSContactAddressLocGetter.cc());
assertEquals("testJSContactForRdapBuilderAndGetter - 21", "イタリア", rdapJSContactAddressLocGetter.country());
assertEquals("testJSContactForRdapBuilderAndGetter - 22", "PI", rdapJSContactAddressLocGetter.sp());
assertEquals("testJSContactForRdapBuilderAndGetter - 23", "ピサ", rdapJSContactAddressLocGetter.city());
assertEquals("testJSContactForRdapBuilderAndGetter - 24", "56124", rdapJSContactAddressLocGetter.pc());
assertEquals("testJSContactForRdapBuilderAndGetter - 25", "モルッツィ通り、1", rdapJSContactAddressLocGetter.street());
}
Test cases are executed using JUnit4 and cover all the features provided.
New scribers and properties have been defined to support the implementation of the extensions to vCard name and address components as dfined in RFC9554. To parse and write vCard instances having such extensions, the methods provided by ez-vcard in the Ezvcard class cannot be used. Similar methods considering those extensions have been defined in the classes VCardParser and VCardWriter.
As opposed to what is stated in section 6.1.1 of RFC6350, ez-vcard doesn't represent both family and given names including multiple text values separated by comma. In both cases, the text values after the first value are ignored.
This jscontact-tools version is compliant with JSContact specification version -08 and JSContact-vCard mapping version -07.
jscontact-tools can be run on Java 8 or 11.
jscontact-tools uses Maven as its build tool.
To build the project: mvn compile
To run the unit tests: mvn test
To build a JAR: mvn package
Two options are available:
- Raising an issue on Issue tracker
- Sending an email to mailto:[email protected]