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