001package ezvcard.io.json; 002 003import java.io.File; 004import java.io.Flushable; 005import java.io.IOException; 006import java.io.OutputStream; 007import java.io.Writer; 008import java.util.List; 009 010import com.fasterxml.jackson.core.JsonGenerator; 011import com.fasterxml.jackson.core.PrettyPrinter; 012 013import ezvcard.VCard; 014import ezvcard.VCardDataType; 015import ezvcard.VCardVersion; 016import ezvcard.io.EmbeddedVCardException; 017import ezvcard.io.SkipMeException; 018import ezvcard.io.StreamWriter; 019import ezvcard.io.scribe.VCardPropertyScribe; 020import ezvcard.parameter.VCardParameters; 021import ezvcard.property.VCardProperty; 022import ezvcard.util.Utf8Writer; 023 024/* 025 Copyright (c) 2012-2018, Michael Angstadt 026 All rights reserved. 027 028 Redistribution and use in source and binary forms, with or without 029 modification, are permitted provided that the following conditions are met: 030 031 1. Redistributions of source code must retain the above copyright notice, this 032 list of conditions and the following disclaimer. 033 2. Redistributions in binary form must reproduce the above copyright notice, 034 this list of conditions and the following disclaimer in the documentation 035 and/or other materials provided with the distribution. 036 037 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 038 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 039 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 040 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 041 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 042 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 043 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 044 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 045 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 046 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 047 048 The views and conclusions contained in the software and documentation are those 049 of the authors and should not be interpreted as representing official policies, 050 either expressed or implied, of the FreeBSD Project. 051 */ 052 053/** 054 * <p> 055 * Writes {@link VCard} objects to a JSON data stream (jCard format). 056 * </p> 057 * <p> 058 * <b>Example:</b> 059 * </p> 060 * 061 * <pre class="brush:java"> 062 * VCard vcard1 = ... 063 * VCard vcard2 = ... 064 * File file = new File("vcard.json"); 065 * JCardWriter writer = null; 066 * try { 067 * writer = new JCardWriter(file); 068 * writer.write(vcard1); 069 * writer.write(vcard2); 070 * } finally { 071 * if (writer != null) writer.close(); 072 * } 073 * </pre> 074 * @author Michael Angstadt 075 * @author Buddy Gorven 076 * @see <a href="http://tools.ietf.org/html/rfc7095">RFC 7095</a> 077 */ 078public class JCardWriter extends StreamWriter implements Flushable { 079 private final JCardRawWriter writer; 080 private final VCardVersion targetVersion = VCardVersion.V4_0; 081 private JsonGenerator generator = null; 082 083 /** 084 * @param out the output stream to write to (UTF-8 encoding will be used) 085 */ 086 public JCardWriter(OutputStream out) { 087 this(new Utf8Writer(out)); 088 } 089 090 /** 091 * @param out the output stream to write to (UTF-8 encoding will be used) 092 * @param wrapInArray true to enclose all written vCards in a JSON array, 093 * false not to 094 */ 095 public JCardWriter(OutputStream out, boolean wrapInArray) { 096 this(new Utf8Writer(out), wrapInArray); 097 } 098 099 /** 100 * @param file the file to write to (UTF-8 encoding will be used) 101 * @throws IOException if there's a problem opening the file 102 */ 103 public JCardWriter(File file) throws IOException { 104 this(new Utf8Writer(file)); 105 } 106 107 /** 108 * @param file the file to write to (UTF-8 encoding will be used) 109 * @param wrapInArray true to enclose all written vCards in a JSON array, 110 * false not to 111 * @throws IOException if there's a problem opening the file 112 */ 113 public JCardWriter(File file, boolean wrapInArray) throws IOException { 114 this(new Utf8Writer(file), wrapInArray); 115 } 116 117 /** 118 * @param writer the writer to write to 119 */ 120 public JCardWriter(Writer writer) { 121 this(writer, false); 122 } 123 124 /** 125 * @param writer the writer to write to 126 * @param wrapInArray true to enclose all written vCards in a JSON array, 127 * false not to 128 */ 129 public JCardWriter(Writer writer, boolean wrapInArray) { 130 this.writer = new JCardRawWriter(writer, wrapInArray); 131 } 132 133 /** 134 * @param generator the generator to write to 135 */ 136 public JCardWriter(JsonGenerator generator) { 137 this.generator = generator; 138 this.writer = new JCardRawWriter(generator); 139 } 140 141 /** 142 * Writes a vCard to the stream. 143 * @param vcard the vCard that is being written 144 * @param properties the properties to write 145 * @throws IOException if there's a problem writing to the output stream 146 * @throws IllegalArgumentException if a scribe hasn't been registered for a 147 * custom property class (see: {@link #registerScribe registerScribe}) 148 */ 149 @Override 150 @SuppressWarnings({ "rawtypes", "unchecked" }) 151 protected void _write(VCard vcard, List<VCardProperty> properties) throws IOException { 152 Object previousValue = getCurrentValue(); 153 154 writer.writeStartVCard(); 155 writer.writeProperty("version", VCardDataType.TEXT, JCardValue.single(targetVersion.getVersion())); 156 157 for (VCardProperty property : properties) { 158 VCardPropertyScribe scribe = index.getPropertyScribe(property); 159 160 //marshal the value 161 JCardValue value; 162 try { 163 value = scribe.writeJson(property); 164 } catch (SkipMeException e) { 165 //property has requested not to be written 166 continue; 167 } catch (EmbeddedVCardException e) { 168 //don't write because jCard does not support embedded vCards 169 continue; 170 } 171 172 String group = property.getGroup(); 173 String name = scribe.getPropertyName().toLowerCase(); 174 VCardParameters parameters = scribe.prepareParameters(property, targetVersion, vcard); 175 VCardDataType dataType = scribe.dataType(property, targetVersion); 176 177 writer.writeProperty(group, name, parameters, dataType, value); 178 } 179 180 writer.writeEndVCard(); 181 182 setCurrentValue(previousValue); 183 } 184 185/** 186 * If this object has a {@link JsonGenerator), and the generator has an 187 * output context, gets the current value of the output context. 188 * 189 * @return the value of the object that is currently being serialized, if 190 * available 191 */ 192 private Object getCurrentValue() { 193 return (generator == null) ? null : generator.getCurrentValue(); 194 } 195 196/** 197 * If this object has a {@link JsonGenerator), and the generator has an 198 * output context, sets the current value of the output context. 199 * 200 * @param value 201 * the object that is currently being serialized 202 */ 203 private void setCurrentValue(Object value) { 204 if (generator != null) { 205 generator.setCurrentValue(value); 206 } 207 } 208 209 @Override 210 protected VCardVersion getTargetVersion() { 211 return targetVersion; 212 } 213 214 /** 215 * Gets whether or not the JSON will be pretty-printed. 216 * @return true if it will be pretty-printed, false if not (defaults to 217 * false) 218 */ 219 public boolean isPrettyPrint() { 220 return writer.isPrettyPrint(); 221 } 222 223 /** 224 * Sets whether or not to pretty-print the JSON. 225 * @param prettyPrint true to pretty-print it, false not to (defaults to 226 * false) 227 */ 228 public void setPrettyPrint(boolean prettyPrint) { 229 writer.setPrettyPrint(prettyPrint); 230 } 231 232 /** 233 * Sets the pretty printer to pretty-print the JSON with. Note that this 234 * method implicitly enables indenting, so {@code setPrettyPrint(true)} does 235 * not also need to be called. 236 * @param prettyPrinter the custom pretty printer (defaults to an instance 237 * of {@link JCardPrettyPrinter}, if {@code setPrettyPrint(true)} has been 238 * called. 239 */ 240 public void setPrettyPrinter(PrettyPrinter prettyPrinter) { 241 writer.setPrettyPrinter(prettyPrinter); 242 } 243 244 /** 245 * Flushes the jCard data stream. 246 * @throws IOException if there's a problem flushing the stream 247 */ 248 public void flush() throws IOException { 249 writer.flush(); 250 } 251 252 /** 253 * Ends the jCard data stream, but does not close the underlying writer. 254 * @throws IOException if there's a problem closing the stream 255 */ 256 public void closeJsonStream() throws IOException { 257 writer.closeJsonStream(); 258 } 259 260 /** 261 * Ends the jCard data stream and closes the underlying writer. 262 * @throws IOException if there's a problem closing the stream 263 */ 264 public void close() throws IOException { 265 writer.close(); 266 } 267}