blob: 98718d6268e2e48a5e1ac9e974cd2ea349b70d64 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.marmotta.commons.sesame.rio.vcard;
import com.google.common.base.Preconditions;
import net.fortuna.ical4j.data.ParserException;
import net.fortuna.ical4j.util.CompatibilityHints;
import net.fortuna.ical4j.vcard.Parameter;
import net.fortuna.ical4j.vcard.Property;
import net.fortuna.ical4j.vcard.VCard;
import net.fortuna.ical4j.vcard.VCardBuilder;
import net.fortuna.ical4j.vcard.parameter.Type;
import net.fortuna.ical4j.vcard.property.*;
import org.openrdf.model.Literal;
import org.openrdf.model.Resource;
import org.openrdf.model.URI;
import org.openrdf.model.ValueFactory;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.RDFHandlerException;
import org.openrdf.rio.RDFParseException;
import org.openrdf.rio.helpers.RDFParserBase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* A parser for parsing VCard files into RDF. Uses the vcard vocabulary (http://www.w3.org/2006/vcard/ns) for
* representing properties.
* <p/>
* Ontology documentation: http://www.w3.org/2006/vcard/ns-2006.html#Name
* <p/>
* Author: Sebastian Schaffert
*/
public class VCardParser extends RDFParserBase {
private static Logger log = LoggerFactory.getLogger(VCardParser.class);
public static final String NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
public static final String NS_VCARD = "http://www.w3.org/2006/vcard/ns#";
private static Map<Property.Id, String> propertyMappings = new HashMap<>();
static {
propertyMappings.put(Property.Id.ADR, "http://www.w3.org/2006/vcard/ns#adr");
propertyMappings.put(Property.Id.AGENT, "http://www.w3.org/2006/vcard/ns#agent");
propertyMappings.put(Property.Id.BDAY, "http://www.w3.org/2006/vcard/ns#bday");
propertyMappings.put(Property.Id.CATEGORIES, "http://www.w3.org/2006/vcard/ns#category");
propertyMappings.put(Property.Id.CLASS, "http://www.w3.org/2006/vcard/ns#class");
propertyMappings.put(Property.Id.EMAIL, "http://www.w3.org/2006/vcard/ns#email");
propertyMappings.put(Property.Id.EXTENDED, "http://www.w3.org/2006/vcard/ns#extended-address");
propertyMappings.put(Property.Id.FN, "http://www.w3.org/2006/vcard/ns#fn");
propertyMappings.put(Property.Id.GEO, "http://www.w3.org/2006/vcard/ns#geo");
propertyMappings.put(Property.Id.KEY, "http://www.w3.org/2006/vcard/ns#key");
propertyMappings.put(Property.Id.LABEL, "http://www.w3.org/2006/vcard/ns#label");
propertyMappings.put(Property.Id.LOGO, "http://www.w3.org/2006/vcard/ns#logo");
propertyMappings.put(Property.Id.N, "http://www.w3.org/2006/vcard/ns#n");
propertyMappings.put(Property.Id.NICKNAME, "http://www.w3.org/2006/vcard/ns#nickname");
propertyMappings.put(Property.Id.NOTE, "http://www.w3.org/2006/vcard/ns#note");
propertyMappings.put(Property.Id.ORG, "http://www.w3.org/2006/vcard/ns#org");
propertyMappings.put(Property.Id.PHOTO, "http://www.w3.org/2006/vcard/ns#photo");
propertyMappings.put(Property.Id.PRODID, "http://www.w3.org/2006/vcard/ns#prodid");
propertyMappings.put(Property.Id.REV, "http://www.w3.org/2006/vcard/ns#rev");
propertyMappings.put(Property.Id.ROLE, "http://www.w3.org/2006/vcard/ns#role");
propertyMappings.put(Property.Id.SORT_STRING,"http://www.w3.org/2006/vcard/ns#sort-string");
propertyMappings.put(Property.Id.SOUND, "http://www.w3.org/2006/vcard/ns#sound");
propertyMappings.put(Property.Id.TEL, "http://www.w3.org/2006/vcard/ns#tel");
propertyMappings.put(Property.Id.TITLE, "http://www.w3.org/2006/vcard/ns#title");
propertyMappings.put(Property.Id.TZ, "http://www.w3.org/2006/vcard/ns#tz");
propertyMappings.put(Property.Id.UID, "http://www.w3.org/2006/vcard/ns#uid");
propertyMappings.put(Property.Id.URL, "http://www.w3.org/2006/vcard/ns#url");
}
/**
* Creates a new RDFParserBase that will use a {@link org.openrdf.model.impl.ValueFactoryImpl} to
* create RDF model objects.
*/
public VCardParser() {
CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING,true);
}
/**
* Creates a new RDFParserBase that will use the supplied ValueFactory to
* create RDF model objects.
*
* @param valueFactory A ValueFactory.
*/
public VCardParser(ValueFactory valueFactory) {
super(valueFactory);
CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING,true);
}
/**
* Gets the RDF format that this parser can parse.
*/
@Override
public RDFFormat getRDFFormat() {
return VCardFormat.FORMAT;
}
/**
* Parses the data from the supplied InputStream, using the supplied baseURI
* to resolve any relative URI references.
*
* @param in The InputStream from which to read the data.
* @param baseURI The URI associated with the data in the InputStream.
* @throws java.io.IOException If an I/O error occurred while data was read from the InputStream.
* @throws org.openrdf.rio.RDFParseException
* If the parser has found an unrecoverable parse error.
* @throws org.openrdf.rio.RDFHandlerException
* If the configured statement handler has encountered an
* unrecoverable error.
*/
@Override
public void parse(InputStream in, String baseURI) throws IOException, RDFParseException, RDFHandlerException {
Preconditions.checkNotNull(baseURI);
setBaseURI(baseURI);
try {
for(VCard card : new VCardBuilder(in).buildAll()) {
parseVCard(card);
}
} catch (ParserException e) {
throw new RDFParseException(e);
}
}
/**
* Parses the data from the supplied Reader, using the supplied baseURI to
* resolve any relative URI references.
*
* @param reader The Reader from which to read the data.
* @param baseURI The URI associated with the data in the InputStream.
* @throws java.io.IOException If an I/O error occurred while data was read from the InputStream.
* @throws org.openrdf.rio.RDFParseException
* If the parser has found an unrecoverable parse error.
* @throws org.openrdf.rio.RDFHandlerException
* If the configured statement handler has encountered an
* unrecoverable error.
*/
@Override
public void parse(Reader reader, String baseURI) throws IOException, RDFParseException, RDFHandlerException {
Preconditions.checkNotNull(baseURI);
setBaseURI(baseURI);
try {
for(VCard card : new VCardBuilder(reader).buildAll()) {
parseVCard(card);
}
} catch (ParserException e) {
throw new RDFParseException(e);
}
}
private void parseVCard(VCard vCard) throws RDFHandlerException, RDFParseException {
URI uri;
// create uri for resource representing the vcard based on the UID if present or on a random UUID
if(vCard.getProperty(Property.Id.UID) != null) {
uri = resolveURI(vCard.getProperty(Property.Id.UID).getValue());
} else {
uri = resolveURI(UUID.randomUUID().toString());
}
Resource t_vcard = createURI(NS_VCARD + "VCard");
URI p_type = createURI(NS_RDF + "type");
rdfHandler.handleStatement(createStatement(uri,p_type,t_vcard));
for(Property p : vCard.getProperties()) {
if(propertyMappings.containsKey(p.getId())) {
URI prop = createURI(propertyMappings.get(p.getId()));
switch (p.getId()) {
case ADR:
createAddress(uri,prop,(Address)p);
break;
case AGENT:
// distinguish text and uri agents
createAgent(uri,prop,(Agent)p);
break;
case CATEGORIES:
for(String category : p.getValue().split(",")) {
Literal literal = createLiteral(category,null,null);
rdfHandler.handleStatement(createStatement(uri, prop, literal));
}
break;
case EMAIL:
URI email = createURI("mailto:"+p.getValue());
rdfHandler.handleStatement(createStatement(uri,prop,email));
break;
case GEO:
createLocation(uri,prop,(Geo)p);
break;
case LOGO:
createLogo(uri,prop,(Logo)p);
break;
case N:
createName(uri,prop,(N)p);
break;
case ORG:
createOrganization(uri,prop,(Org)p);
break;
case PHOTO:
createPhoto(uri, prop, (Photo) p);
break;
case SOUND:
createSound(uri, prop, (Sound) p);
break;
case TEL:
createTel(uri, prop, (Telephone) p);
break;
case URL:
URI url = createURI(p.getValue());
rdfHandler.handleStatement(createStatement(uri,prop,url));
break;
default:
Literal literal = createLiteral(p.getValue(),null,null);
rdfHandler.handleStatement(createStatement(uri, prop, literal));
}
} else {
log.warn("unknown property type {} with value {} not added to RDF representation",p.getId(), p.getValue());
}
}
}
private void createAddress(URI uri, URI prop, Address adr) throws RDFHandlerException, RDFParseException {
Resource r_adr = createBNode();
Resource t_adr = createURI(NS_VCARD + "Address");
URI p_type = createURI(NS_RDF + "type");
rdfHandler.handleStatement(createStatement(r_adr,p_type,t_adr));
if(adr.getCountry() != null) {
URI p_country = createURI(NS_VCARD + "country-name");
Literal v_country = createLiteral(adr.getCountry(),null,null);
rdfHandler.handleStatement(createStatement(r_adr,p_country,v_country));
}
if(adr.getExtended() != null && !"".equals(adr.getExtended().trim())) {
URI p_ext = createURI(NS_VCARD + "extended-address");
Literal v_ext = createLiteral(adr.getExtended(),null,null);
rdfHandler.handleStatement(createStatement(r_adr,p_ext,v_ext));
}
if(adr.getLocality() != null && !"".equals(adr.getLocality().trim())) {
URI p_locality = createURI(NS_VCARD + "locality");
Literal v_locality = createLiteral(adr.getLocality(),null,null);
rdfHandler.handleStatement(createStatement(r_adr,p_locality,v_locality));
}
if(adr.getPoBox() != null && !"".equals(adr.getPoBox().trim())) {
URI p_pobox = createURI(NS_VCARD + "post-office-box");
Literal v_pobox = createLiteral(adr.getPoBox(),null,null);
rdfHandler.handleStatement(createStatement(r_adr,p_pobox,v_pobox));
}
if(adr.getPostcode() != null && !"".equals(adr.getPostcode().trim())) {
URI p_postcode = createURI(NS_VCARD + "postal-code");
Literal v_postcode = createLiteral(adr.getPostcode(),null,null);
rdfHandler.handleStatement(createStatement(r_adr,p_postcode,v_postcode));
}
if(adr.getRegion() != null && !"".equals(adr.getRegion().trim())) {
URI p_region = createURI(NS_VCARD + "region");
Literal v_region = createLiteral(adr.getRegion(),null,null);
rdfHandler.handleStatement(createStatement(r_adr,p_region,v_region));
}
if(adr.getStreet() != null) {
URI p_street = createURI(NS_VCARD + "street-address");
Literal v_street = createLiteral(adr.getStreet(),null,null);
rdfHandler.handleStatement(createStatement(r_adr,p_street,v_street));
}
if(adr.getParameter(Parameter.Id.TYPE) != null) {
Type type = (Type)adr.getParameter(Parameter.Id.TYPE);
for(String value : type.getTypes()) {
if("HOME".equals(value)) {
URI p_home = createURI(NS_VCARD + "homeAdr");
rdfHandler.handleStatement(createStatement(uri,p_home,r_adr));
} else if("WORK".equals(value)) {
URI p_work = createURI(NS_VCARD + "workAdr");
rdfHandler.handleStatement(createStatement(uri,p_work,r_adr));
} else {
rdfHandler.handleStatement(createStatement(uri,prop,r_adr));
}
break; // only first one
}
} else {
rdfHandler.handleStatement(createStatement(uri,prop,r_adr));
}
}
private void createAgent(URI uri, URI prop, Agent agent) throws RDFHandlerException, RDFParseException {
if(agent.getUri() != null) {
URI r_agent = createURI(agent.getUri().toString());
rdfHandler.handleStatement(createStatement(uri,prop,r_agent));
} else {
log.warn("ignoring agent relation, since agent cannot be resolved");
}
}
private void createLocation(URI uri, URI prop, Geo geo) throws RDFHandlerException, RDFParseException {
Resource r_location = createBNode();
Resource t_adr = createURI(NS_VCARD + "Location");
URI p_type = createURI(NS_RDF + "type");
rdfHandler.handleStatement(createStatement(r_location,p_type,t_adr));
URI p_latitute = createURI(NS_VCARD+"latitude");
URI p_longitude = createURI(NS_VCARD+"longitude");
URI t_decimal = createURI("http://www.w3.org/2001/XMLSchema#double");
Literal v_latitude = createLiteral(geo.getLatitude().toPlainString(),null,t_decimal);
Literal v_longitude = createLiteral(geo.getLongitude().toPlainString(), null, t_decimal);
rdfHandler.handleStatement(createStatement(r_location,p_latitute,v_latitude));
rdfHandler.handleStatement(createStatement(r_location,p_longitude,v_longitude));
rdfHandler.handleStatement(createStatement(uri,prop,r_location));
}
private void createLogo(URI uri, URI prop, Logo logo) throws RDFHandlerException, RDFParseException {
if(logo.getUri() != null) {
URI r_logo = createURI(logo.getUri().toString());
rdfHandler.handleStatement(createStatement(uri,prop,r_logo));
} else {
log.warn("ignoring logo relation, since binary logos are not supported in RDF");
}
}
private void createName(URI uri, URI prop, N name) throws RDFHandlerException, RDFParseException {
Resource r_name = createBNode();
Resource t_name = createURI(NS_VCARD + "Name");
URI p_type = createURI(NS_RDF + "type");
rdfHandler.handleStatement(createStatement(r_name,p_type,t_name));
if(name.getFamilyName() != null) {
URI p_family_name = createURI(NS_VCARD + "family-name");
Literal v_family_name = createLiteral(name.getFamilyName(),null,null);
rdfHandler.handleStatement(createStatement(r_name,p_family_name,v_family_name));
}
if(name.getGivenName() != null) {
URI p_given_name = createURI(NS_VCARD + "given-name");
Literal v_given_name = createLiteral(name.getGivenName(),null,null);
rdfHandler.handleStatement(createStatement(r_name,p_given_name,v_given_name));
}
if(name.getAdditionalNames() != null && name.getAdditionalNames().length > 0) {
URI p_additional_name = createURI(NS_VCARD + "additional-name");
for(String additionalName : name.getAdditionalNames()) {
if(!"".equals(additionalName)) {
Literal v_additional_name = createLiteral(additionalName,null,null);
rdfHandler.handleStatement(createStatement(r_name,p_additional_name,v_additional_name));
}
}
}
if(name.getPrefixes() != null && name.getPrefixes().length > 0) {
URI p_prefix = createURI(NS_VCARD + "honorific-prefix");
for(String namePrefix : name.getPrefixes()) {
if(!"".equals(namePrefix)) {
Literal v_prefix = createLiteral(namePrefix,null,null);
rdfHandler.handleStatement(createStatement(r_name,p_prefix,v_prefix));
}
}
}
if(name.getSuffixes() != null && name.getSuffixes().length > 0) {
URI p_suffix = createURI(NS_VCARD + "honorific-suffix");
for(String nameSuffix : name.getSuffixes()) {
if(!"".equals(nameSuffix)) {
Literal v_suffix = createLiteral(nameSuffix,null,null);
rdfHandler.handleStatement(createStatement(r_name,p_suffix,v_suffix));
}
}
}
rdfHandler.handleStatement(createStatement(uri,prop,r_name));
}
private void createOrganization(URI uri, URI prop, Org org) throws RDFHandlerException, RDFParseException {
for(String orgName : org.getValues()) {
Resource r_name = createBNode();
Resource t_org = createURI(NS_VCARD + "Organization");
URI p_type = createURI(NS_RDF + "type");
rdfHandler.handleStatement(createStatement(r_name,p_type,t_org));
URI p_name = createURI(NS_VCARD + "organization-name");
Literal v_name = createLiteral(orgName,null,null);
rdfHandler.handleStatement(createStatement(r_name,p_name,v_name));
rdfHandler.handleStatement(createStatement(uri,prop,r_name));
}
}
private void createPhoto(URI uri, URI prop, Photo photo) throws RDFHandlerException, RDFParseException {
if(photo.getUri() != null) {
URI r_logo = createURI(photo.getUri().toString());
rdfHandler.handleStatement(createStatement(uri,prop,r_logo));
} else {
log.warn("ignoring photo relation, since binary photos are not supported in RDF");
}
}
private void createSound(URI uri, URI prop, Sound sound) throws RDFHandlerException, RDFParseException {
if(sound.getUri() != null) {
URI r_logo = createURI(sound.getUri().toString());
rdfHandler.handleStatement(createStatement(uri,prop,r_logo));
} else {
log.warn("ignoring photo relation, since binary photos are not supported in RDF");
}
}
private void createTel(URI uri, URI prop, Telephone telephone) throws RDFHandlerException, RDFParseException {
URI r_tel;
if(telephone.getUri() != null) {
r_tel = createURI(telephone.getUri().toString());
} else {
r_tel = createURI("tel:"+telephone.getValue());
}
if(telephone.getParameter(Parameter.Id.TYPE) != null) {
Type type = (Type)telephone.getParameter(Parameter.Id.TYPE);
for(String value : type.getTypes()) {
if("HOME".equals(value)) {
URI p_home = createURI(NS_VCARD + "homeTel");
rdfHandler.handleStatement(createStatement(uri,p_home,r_tel));
} else if("WORK".equals(value)) {
URI p_work = createURI(NS_VCARD + "workTel");
rdfHandler.handleStatement(createStatement(uri,p_work,r_tel));
} else if("CELL".equals(value)) {
URI p_work = createURI(NS_VCARD + "mobileTel");
rdfHandler.handleStatement(createStatement(uri,p_work,r_tel));
} else {
rdfHandler.handleStatement(createStatement(uri,prop,r_tel));
}
break; // only first one
}
} else {
rdfHandler.handleStatement(createStatement(uri,prop,r_tel));
}
}
}