mangstadt/ez-vcard

Custom getter in VCardProperty

Opened this issue · 9 comments

I am trying to retrieve all the values by calling a custom method on a VCardProperty. (Address, Email, Categories...)
I'm not a Java specialist, but I can't manage to do this without modifying the VCardProperty class.
Thank you for your help.

Not quite sure what you're trying to accomplish. The VCard.toString() method will generate a string that displays the contents of each property object.

VCard vcard = new VCard();
vcard.setFormattedName("John Doe");
Address adr = new Address();
adr.setStreetAddress("123 Main St");
adr.setLocality("New York");
adr.setRegion("NY");
adr.setPostalCode("12345");
vcard.addAddress(adr);
System.out.println(vcard.toString());

Prints:

version=3.0
ezvcard.property.FormattedName [ group=null | parameters={} | value=John Doe ]
ezvcard.property.Address [ group=null | parameters={} | poBoxes=[] | extendedAddresses=[] | streetAddresses=[123 Main St] | localities=[New York] | regions=[NY] | postalCodes=[12345] | countries=[] ]

Hi mangstadt,

In fact I am trying to import cards into a database:
To complete this task I need:

  • Treat nulls for each value independently.
  • If the value is a list extract the first value.
  • If needed convert data to string type.
  • Uniquely identify the value.
  • Accumulate the values in order to update with a batch.

And this for each value of each property, independently of each other...

If I have the possibility to use a customized getter here is the code I get to import:

  • For each property it is 1 line of code per value to process. See Address
  • For global processing, it is a loop of 30 lines

What will the code look like if I don't have a custom getter?
I'm surprised that I'm the first to claim this....

I would like to add that this mode of use has the advantage of separating the processing of data (class Address, Email...) from the import logic (global processing).
It makes it possible to add processing of new properties without having to modify the import logic...

EDIT: And this is important if i want the user to be able to choose which vCard properties he wants to import

You could serialize the vCard using Ezvcard.write() and store that in the database. ez-vcard is pretty good about round-tripping.

I need to be more specific about the purpose.
In fact, this is part of a LibreOffice extension allowing mass mailing by email with your vCard address book.

This extension vCardOOo has some particularities:

  • It is seen by LibreOffice OpenOffice as a database driver.
  • It is synchronized with an account of a NextCloud server during its use after the connection of the user to the database.
  • It is only read-only (in fact the data displayed by the driver are only HsqlDB views)
  • These views are composed of columns calculated dynamically from a configuration file allowing to choose the vcard properties to import.
  • vCard typed properties will produce as many columns in HsqlDB views as there are types and values.
  • I would like to be able to drive views and import data from configuration files.

I already have an extension that does exactly the same thing but with Google contacts: gContactOOo
And I'm about to finish the one for Microsoft Contact: mContactOOo

All these extensions are written in Python except the parsing part for vCardOOo where I use ez-vcard which is the best API on vCard. ;-)

So I admit that my need is really specific, but if I want to be able to follow my specifications I will just use my modified version of ez-vcard (all my jar libraries are embedded in the extension oxt files)

Using sub-classes can be problematic. I might do something like this instead:

public Map<String, String> getPropertiesValue(VCardProperty property) {
  if (property instanceof Address) {
    return getPropertiesValue((Address)property);
  }
  if (property instanceof Categories) {
    return getPropertiesValue((Categories)property);
  }
  //etc
}

public Map<String, String> getPropertiesValue(Address property) {
  Map<String, String> values = new LinkedHashMap<>();
  if (getPoBox() != null) values.put("poBox", getPoBox());
  //...
  return values;
}

Can you confirm that if I follow your recommendations then it is no longer necessary to modify the VCardProperty class, but only the Address, Email, etc. subclasses?

In the implementation of my class Address which extends the class ezvcard.property.Address what are the methods that must be implemented?

Thank you for these hints.

If your goal is to convert each vCard property object (Address, Categories, etc) to a Map<String,String> object, then you shouldn't need to subclass anything! For example:

VCard vcard = ...
for (VCardProperty property : vcard.getProperties()) {
  Map<String,String> values = getPropertiesValue(property);
}

Yes in fact I am looking to have a single method whatever the property to obtain 3 things:

  • property values as a Map<String,String> object.
  • the categories as a String[] array if the property is a ezvcard.property.Categories.
  • the types as a String[] array if the property is typed.