001 package ezvcard.io.html; 002 003 import java.io.File; 004 import java.io.FileWriter; 005 import java.io.IOException; 006 import java.io.OutputStream; 007 import java.io.OutputStreamWriter; 008 import java.io.StringWriter; 009 import java.io.Writer; 010 import java.util.ArrayList; 011 import java.util.HashMap; 012 import java.util.List; 013 import java.util.Map; 014 015 import ezvcard.Ezvcard; 016 import ezvcard.VCard; 017 import ezvcard.io.scribe.ScribeIndex; 018 import ezvcard.parameter.ImageType; 019 import ezvcard.property.Photo; 020 import ezvcard.util.DataUri; 021 import ezvcard.util.IOUtils; 022 import freemarker.template.Configuration; 023 import freemarker.template.DefaultObjectWrapper; 024 import freemarker.template.Template; 025 import freemarker.template.TemplateException; 026 027 /* 028 Copyright (c) 2013, Michael Angstadt 029 All rights reserved. 030 031 Redistribution and use in source and binary forms, with or without 032 modification, are permitted provided that the following conditions are met: 033 034 1. Redistributions of source code must retain the above copyright notice, this 035 list of conditions and the following disclaimer. 036 2. Redistributions in binary form must reproduce the above copyright notice, 037 this list of conditions and the following disclaimer in the documentation 038 and/or other materials provided with the distribution. 039 040 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 041 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 042 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 043 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 044 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 045 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 046 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 047 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 048 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 049 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 050 051 The views and conclusions contained in the software and documentation are those 052 of the authors and should not be interpreted as representing official policies, 053 either expressed or implied, of the FreeBSD Project. 054 */ 055 056 /** 057 * <p> 058 * Writes {@link VCard} objects to a templated HTML page (hCard format). 059 * </p> 060 * <p> 061 * <b>Example:</b> 062 * 063 * <pre class="brush:java"> 064 * VCard vcard1 = ... 065 * VCard vcard2 = ... 066 * 067 * HCardPage page = new HCardPage(); 068 * page.add(vcard1); 069 * page.add(vcard2); 070 * 071 * File file = new File("hcard.html"); 072 * page.write(file); 073 * </pre> 074 * 075 * </p> 076 * @author Michael Angstadt 077 * @see <a 078 * href="http://microformats.org/wiki/hcard">http://microformats.org/wiki/hcard</a> 079 */ 080 public class HCardPage { 081 private static final Template template; 082 static { 083 Configuration cfg = new Configuration(); 084 cfg.setClassForTemplateLoading(HCardPage.class, ""); 085 cfg.setObjectWrapper(new DefaultObjectWrapper()); 086 cfg.setWhitespaceStripping(true); 087 try { 088 template = cfg.getTemplate("hcard-template.html"); 089 } catch (IOException e) { 090 //should never be thrown because it's always on the classpath 091 throw new RuntimeException(e); 092 } 093 } 094 095 private final List<VCard> vcards = new ArrayList<VCard>(); 096 097 /** 098 * Adds a vCard to the HTML page. 099 * @param vcard the vCard to add 100 */ 101 public void add(VCard vcard) { 102 vcards.add(vcard); 103 } 104 105 /** 106 * Writes the HTML document to a string. 107 * @return the HTML document 108 */ 109 public String write() { 110 StringWriter sw = new StringWriter(); 111 try { 112 write(sw); 113 } catch (IOException e) { 114 //never thrown because we're writing to a string 115 } 116 return sw.toString(); 117 } 118 119 /** 120 * Writes the HTML document to an output stream. 121 * @param out the output stream 122 * @throws IOException if there's a problem writing to the output stream 123 */ 124 public void write(OutputStream out) throws IOException { 125 write(new OutputStreamWriter(out)); 126 } 127 128 /** 129 * Writes the HTML document to a file. 130 * @param file the file 131 * @throws IOException if there's a problem writing to the file 132 */ 133 public void write(File file) throws IOException { 134 FileWriter writer = null; 135 try { 136 writer = new FileWriter(file); 137 write(writer); 138 } finally { 139 IOUtils.closeQuietly(writer); 140 } 141 } 142 143 /** 144 * Writes the HTML document to a writer. 145 * @param writer the writer 146 * @throws IOException if there's a problem writing to the writer 147 */ 148 public void write(Writer writer) throws IOException { 149 Map<String, Object> map = new HashMap<String, Object>(); 150 map.put("vcards", vcards); 151 map.put("dataUri", new DataUriGenerator()); 152 map.put("translucentBg", readImage("translucent-bg.png", ImageType.PNG)); 153 map.put("noProfile", readImage("no-profile.png", ImageType.PNG)); 154 map.put("ezVCardVersion", Ezvcard.VERSION); 155 map.put("ezVCardUrl", Ezvcard.URL); 156 map.put("scribeIndex", new ScribeIndex()); 157 try { 158 template.process(map, writer); 159 } catch (TemplateException e) { 160 //this should never be thrown because we're always using the same template (it is hard-coded and cannot be changed by the user) 161 throw new RuntimeException(e); 162 } 163 writer.flush(); 164 } 165 166 /** 167 * Reads an image from the classpath. 168 * @param name the file name, relative to this class 169 * @param mediaType the media type of the image 170 * @return the image 171 * @throws IOException 172 */ 173 private Photo readImage(String name, ImageType mediaType) throws IOException { 174 return new Photo(getClass().getResourceAsStream(name), mediaType); 175 } 176 177 /** 178 * Generates data URIs for the freemarker template. 179 */ 180 public static class DataUriGenerator { 181 public String generate(String contentType, byte[] data) { 182 return new DataUri(contentType, data).toString(); 183 } 184 } 185 }