001 002/* 003 * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. 004 * 005 * $Id$ 006 */ 007 008package org.dellroad.jibxbindings.vcard; 009 010import java.io.IOException; 011 012import javax.xml.parsers.DocumentBuilderFactory; 013import javax.xml.parsers.ParserConfigurationException; 014 015import org.jibx.extras.DomElementMapper; 016import org.jibx.runtime.IMarshallingContext; 017import org.jibx.runtime.IUnmarshallingContext; 018import org.jibx.runtime.JiBXException; 019import org.w3c.dom.Document; 020import org.w3c.dom.Element; 021 022import ezvcard.VCard; 023import ezvcard.io.xml.XCardDocument; 024 025/** 026 * JiBX marshaller/unmarshaller for a single {@link VCard}. 027 * 028 * <p> 029 * Example of use: 030 * <pre> 031 * <mapping ... > 032 * <namespace uri="urn:ietf:params:xml:ns:vcard-4.0" prefix="vc"/> 033 * ... 034 * <structure name="ContactInfo"> 035 * <structure name="vcard" field="contactInfo" ns="urn:ietf:params:xml:ns:vcard-4.0" 036 * marshaller="org.dellroad.jibxbindings.vcard.VCardMarshaller" 037 * unmarshaller="org.dellroad.jibxbindings.vcard.VCardMarshaller"/> 038 * </structure> 039 * ... 040 * </mapping> 041 * </pre> 042 * </p> 043 * 044 * @see <a href="https://ez-vcard.googlecode.com">The EZ-vCard Project</a> 045 * @see <a href="https://tools.ietf.org/html/rfc6351">RFC 6351</a> 046 */ 047public class VCardMarshaller extends DomElementMapper { 048 049 public static final String VCARD_NAMESPACE_URI = "urn:ietf:params:xml:ns:vcard-4.0"; 050 public static final String VCARDS_ELEMENT_NAME = "vcards"; 051 public static final String VCARD_ELEMENT_NAME = "vcard"; 052 053 public VCardMarshaller() throws JiBXException { 054 this(VCARD_NAMESPACE_URI, 0, VCARD_ELEMENT_NAME); 055 } 056 057 public VCardMarshaller(String uri, int index, String name) throws JiBXException { 058 super(uri, index, name); 059 } 060 061 @Override 062 public void marshal(Object obj, IMarshallingContext ctx) throws JiBXException { 063 064 // Handle null 065 if (obj == null) 066 return; 067 068 // Convert to XML 069 final XCardDocument xcard = new XCardDocument(); 070 xcard.add((VCard)obj); 071 super.marshal(xcard.getDocument().getDocumentElement().getFirstChild(), ctx); 072 } 073 074 @Override 075 public VCard unmarshal(Object obj, IUnmarshallingContext ctx) throws JiBXException { 076 077 // Get XML element 078 final Element element = (Element)super.unmarshal(obj, ctx); 079 080 // Handle null 081 if (element == null) 082 return null; 083 084 // Create temporary XML <vcards> document 085 final Document doc; 086 try { 087 doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); 088 } catch (ParserConfigurationException e) { 089 throw new RuntimeException("unexpected exception", e); 090 } 091 final Element vcards = doc.createElementNS(VCARD_NAMESPACE_URI, VCARDS_ELEMENT_NAME); 092 doc.appendChild(vcards); 093 094 // Append <vcard> node 095 vcards.appendChild(doc.importNode(element, true)); 096 097 // Read it 098 try { 099 return new XCardDocument(doc).reader().readNext(); 100 } catch (IOException e) { 101 throw new RuntimeException("unexpected exception", e); 102 } 103 } 104 105 /** 106 * Clone a {@link VCard} by converting it to XML and back. 107 * This method works around the fact that {@link VCard} does not implement {@link Cloneable}. 108 * 109 * @throws IllegalArgumentException if {@code vcard} is null 110 */ 111 public static VCard clone(VCard vcard) { 112 113 // Sanity check 114 if (vcard == null) 115 throw new IllegalArgumentException("null vcard"); 116 117 // Convert to XML 118 final XCardDocument xcard = new XCardDocument(); 119 xcard.add(vcard); 120 final Document doc = xcard.getDocument(); 121 122 // Convert from XML 123 try { 124 return new XCardDocument(doc).reader().readNext(); 125 } catch (IOException e) { 126 throw new RuntimeException("unexpected exception", e); 127 } 128 } 129} 130