001package ezvcard.io.chain; 002 003import java.io.File; 004import java.io.IOException; 005import java.io.OutputStream; 006import java.io.StringWriter; 007import java.io.Writer; 008import java.util.Collection; 009 010import ezvcard.Ezvcard; 011import ezvcard.VCard; 012import ezvcard.VCardVersion; 013import ezvcard.io.scribe.VCardPropertyScribe; 014import ezvcard.io.text.TargetApplication; 015import ezvcard.io.text.VCardWriter; 016import ezvcard.property.Address; 017import ezvcard.property.StructuredName; 018import ezvcard.property.VCardProperty; 019 020/* 021 Copyright (c) 2012-2018, Michael Angstadt 022 All rights reserved. 023 024 Redistribution and use in source and binary forms, with or without 025 modification, are permitted provided that the following conditions are met: 026 027 1. Redistributions of source code must retain the above copyright notice, this 028 list of conditions and the following disclaimer. 029 2. Redistributions in binary form must reproduce the above copyright notice, 030 this list of conditions and the following disclaimer in the documentation 031 and/or other materials provided with the distribution. 032 033 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 034 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 035 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 036 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 037 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 039 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 040 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 041 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 042 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 043 */ 044 045/** 046 * Chainer class for writing traditional, plain-text vCards. 047 * @see Ezvcard#write(Collection) 048 * @see Ezvcard#write(VCard...) 049 * @author Michael Angstadt 050 */ 051public class ChainingTextWriter extends ChainingWriter<ChainingTextWriter> { 052 private VCardVersion version; 053 private boolean caretEncoding = false; 054 private Boolean includeTrailingSemicolons; 055 private boolean foldLines = true; 056 private TargetApplication targetApplication; 057 058 /** 059 * @param vcards the vCards to write 060 */ 061 public ChainingTextWriter(Collection<VCard> vcards) { 062 super(vcards); 063 } 064 065 /** 066 * <p> 067 * Sets the version that all the vCards will be marshalled to. The version 068 * that is attached to each individual {@link VCard} object will be ignored. 069 * </p> 070 * <p> 071 * If no version is passed into this method, the writer will look at the 072 * version attached to each individual {@link VCard} object and marshal it 073 * to that version. And if a {@link VCard} object has no version attached to 074 * it, then it will be marshalled to version 3.0. 075 * </p> 076 * @param version the version to marshal the vCards to 077 * @return this 078 */ 079 public ChainingTextWriter version(VCardVersion version) { 080 this.version = version; 081 return this; 082 } 083 084 /** 085 * Sets whether the writer will use circumflex accent encoding for vCard 3.0 086 * and 4.0 parameter values (disabled by default). 087 * @param enable true to use circumflex accent encoding, false not to 088 * @return this 089 * @see VCardWriter#setCaretEncodingEnabled(boolean) 090 * @see <a href="http://tools.ietf.org/html/rfc6868">RFC 6868</a> 091 */ 092 public ChainingTextWriter caretEncoding(boolean enable) { 093 this.caretEncoding = enable; 094 return this; 095 } 096 097 /** 098 * <p> 099 * Sets whether to include trailing semicolon delimiters for structured 100 * property values whose list of values end with null or empty values. 101 * Examples of properties that use structured values are 102 * {@link StructuredName} and {@link Address}. 103 * </p> 104 * <p> 105 * This setting exists for compatibility reasons and should not make a 106 * difference to consumers that correctly implement the vCard grammar. 107 * </p> 108 * @param include true to include the trailing semicolons, false not to, 109 * null to use the default behavior (defaults to false for vCard versions 110 * 2.1 and 3.0 and true for vCard version 4.0) 111 * @return this 112 * @see <a href="https://github.com/mangstadt/ez-vcard/issues/57">Issue 113 * 57</a> 114 */ 115 public ChainingTextWriter includeTrailingSemicolons(Boolean include) { 116 this.includeTrailingSemicolons = include; 117 return this; 118 } 119 120 /** 121 * <p> 122 * Sets whether to fold long lines. Line folding is when long lines are 123 * split up into multiple lines. No data is lost or changed when a line is 124 * folded. 125 * </p> 126 * <p> 127 * Line folding is enabled by default. If the vCard consumer is not parsing 128 * your vCards properly, disabling line folding may help. 129 * </p> 130 * @param foldLines true to enable line folding, false to disable it 131 * (defaults to true) 132 * @return this 133 */ 134 public ChainingTextWriter foldLines(boolean foldLines) { 135 this.foldLines = foldLines; 136 return this; 137 } 138 139 /** 140 * <p> 141 * Sets the application that the vCards will be targeted for. 142 * </p> 143 * <p> 144 * Some vCard consumers do not completely adhere to the vCard specifications 145 * and require their vCards to be formatted in a specific way. See the 146 * {@link TargetApplication} class for a list of these applications. 147 * </p> 148 * @param targetApplication the target application or null if the vCards do 149 * not require any special processing (defaults to null) 150 * @return this 151 * @see VCardWriter#setTargetApplication(TargetApplication) 152 */ 153 public ChainingTextWriter targetApplication(TargetApplication targetApplication) { 154 this.targetApplication = targetApplication; 155 return this; 156 } 157 158 @Override 159 public ChainingTextWriter prodId(boolean include) { 160 return super.prodId(include); 161 } 162 163 @Override 164 public ChainingTextWriter versionStrict(boolean versionStrict) { 165 return super.versionStrict(versionStrict); 166 } 167 168 @Override 169 public ChainingTextWriter register(VCardPropertyScribe<? extends VCardProperty> scribe) { 170 return super.register(scribe); 171 } 172 173 /** 174 * Writes the vCards to a string. 175 * @return the vCard string 176 */ 177 public String go() { 178 StringWriter sw = new StringWriter(); 179 try { 180 go(sw); 181 } catch (IOException e) { 182 //should never be thrown because we're writing to a string 183 throw new RuntimeException(e); 184 } 185 return sw.toString(); 186 } 187 188 /** 189 * Writes the vCards to an output stream. 190 * @param out the output stream to write to 191 * @throws IOException if there's a problem writing to the output stream 192 */ 193 public void go(OutputStream out) throws IOException { 194 go(new VCardWriter(out, getVCardWriterConstructorVersion())); 195 } 196 197 /** 198 * Writes the vCards to a file. If the file exists, it will be overwritten. 199 * @param file the file to write to 200 * @throws IOException if there's a problem writing to the file 201 */ 202 public void go(File file) throws IOException { 203 go(file, false); 204 } 205 206 /** 207 * Writes the vCards to a file. 208 * @param file the file to write to 209 * @param append true to append onto the end of the file, false to overwrite 210 * it 211 * @throws IOException if there's a problem writing to the file 212 */ 213 public void go(File file, boolean append) throws IOException { 214 VCardWriter writer = new VCardWriter(file, append, getVCardWriterConstructorVersion()); 215 try { 216 go(writer); 217 } finally { 218 writer.close(); 219 } 220 } 221 222 /** 223 * Writes the vCards to a writer. 224 * @param writer the writer to write to 225 * @throws IOException if there's a problem writing to the writer 226 */ 227 public void go(Writer writer) throws IOException { 228 go(new VCardWriter(writer, getVCardWriterConstructorVersion())); 229 } 230 231 private void go(VCardWriter writer) throws IOException { 232 writer.setAddProdId(prodId); 233 writer.setCaretEncodingEnabled(caretEncoding); 234 writer.setVersionStrict(versionStrict); 235 writer.setIncludeTrailingSemicolons(includeTrailingSemicolons); 236 if (!foldLines) { 237 writer.getVObjectWriter().getFoldedLineWriter().setLineLength(null); 238 } 239 writer.setTargetApplication(targetApplication); 240 if (index != null) { 241 writer.setScribeIndex(index); 242 } 243 244 for (VCard vcard : vcards) { 245 if (version == null) { 246 //use the version that's assigned to each individual vCard 247 VCardVersion vcardVersion = vcard.getVersion(); 248 if (vcardVersion == null) { 249 vcardVersion = VCardVersion.V3_0; 250 } 251 writer.setTargetVersion(vcardVersion); 252 } 253 writer.write(vcard); 254 writer.flush(); 255 } 256 } 257 258 /** 259 * <p> 260 * Gets the {@link VCardVersion} object to pass into the {@link VCardWriter} 261 * constructor. The constructor does not allow a null version, so this 262 * method ensures that a non-null version is passed in. 263 * </p> 264 * <p> 265 * If the user hasn't chosen a version, the version that is passed into the 266 * constructor doesn't matter. This is because the writer's target version 267 * is reset every time a vCard is written (see the {@link #go(VCardWriter)} 268 * method). 269 * </p> 270 * @return the version to pass into the constructor 271 */ 272 private VCardVersion getVCardWriterConstructorVersion() { 273 return (version == null) ? VCardVersion.V3_0 : version; 274 } 275}