001 package ezvcard;
002
003 import java.io.File;
004 import java.io.FileWriter;
005 import java.io.IOException;
006 import java.io.InputStream;
007 import java.io.InputStreamReader;
008 import java.io.OutputStream;
009 import java.io.OutputStreamWriter;
010 import java.io.Reader;
011 import java.io.StringWriter;
012 import java.io.Writer;
013 import java.net.URL;
014 import java.nio.charset.Charset;
015 import java.util.ArrayList;
016 import java.util.Arrays;
017 import java.util.Collection;
018 import java.util.List;
019 import java.util.Properties;
020
021 import javax.xml.transform.TransformerException;
022
023 import org.w3c.dom.Document;
024 import org.xml.sax.SAXException;
025
026 import com.fasterxml.jackson.core.JsonParseException;
027
028 import ezvcard.io.html.HCardPage;
029 import ezvcard.io.html.HCardReader;
030 import ezvcard.io.json.JCardParseException;
031 import ezvcard.io.json.JCardReader;
032 import ezvcard.io.json.JCardWriter;
033 import ezvcard.io.scribe.ScribeIndex;
034 import ezvcard.io.scribe.VCardPropertyScribe;
035 import ezvcard.io.text.VCardReader;
036 import ezvcard.io.text.VCardWriter;
037 import ezvcard.io.xml.XCardDocument;
038 import ezvcard.property.VCardProperty;
039 import ezvcard.util.IOUtils;
040
041 /*
042 Copyright (c) 2013, Michael Angstadt
043 All rights reserved.
044
045 Redistribution and use in source and binary forms, with or without
046 modification, are permitted provided that the following conditions are met:
047
048 1. Redistributions of source code must retain the above copyright notice, this
049 list of conditions and the following disclaimer.
050 2. Redistributions in binary form must reproduce the above copyright notice,
051 this list of conditions and the following disclaimer in the documentation
052 and/or other materials provided with the distribution.
053
054 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
055 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
056 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
057 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
058 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
059 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
060 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
061 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
062 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
063 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
064
065 The views and conclusions contained in the software and documentation are those
066 of the authors and should not be interpreted as representing official policies,
067 either expressed or implied, of the FreeBSD Project.
068 */
069
070 /**
071 * <p>
072 * Contains chaining factory methods for parsing/writing vCards. They are
073 * convenience methods that make use of the following classes:
074 * </p>
075 *
076 *
077 * <table border="1">
078 * <tr>
079 * <th></th>
080 * <th>Reading</th>
081 * <th>Writing</th>
082 * </tr>
083 * <tr>
084 * <th>Plain text</th>
085 * <td>{@link VCardReader}</td>
086 * <td>{@link VCardWriter}</td>
087 * </tr>
088 * <tr>
089 * <th>XML</th>
090 * <td>{@link XCardDocument}</td>
091 * <td>{@link XCardDocument}</td>
092 * </tr>
093 * <tr>
094 * <th>HTML</th>
095 * <td>{@link HCardReader}</td>
096 * <td>{@link HCardPage}</td>
097 * </tr>
098 * <tr>
099 * <th>JSON</th>
100 * <td>{@link JCardReader}</td>
101 * <td>{@link JCardWriter}</td>
102 * </tr>
103 * </table>
104 * @author Michael Angstadt
105 */
106 public class Ezvcard {
107 /**
108 * The version of the library.
109 */
110 public static final String VERSION;
111
112 /**
113 * The project webpage.
114 */
115 public static final String URL;
116
117 static {
118 InputStream in = null;
119 try {
120 in = Ezvcard.class.getResourceAsStream("/ez-vcard.properties");
121 Properties props = new Properties();
122 props.load(in);
123
124 VERSION = props.getProperty("version");
125 URL = props.getProperty("url");
126 } catch (IOException e) {
127 throw new RuntimeException(e);
128 } finally {
129 IOUtils.closeQuietly(in);
130 }
131 }
132
133 /**
134 * <p>
135 * Parses plain text vCards.
136 * </p>
137 * <p>
138 * Use {@link VCardReader} for more control over the parsing.
139 * </p>
140 * @param str the vCard string
141 * @return chainer object for completing the parse operation
142 * @see VCardReader
143 * @see <a href="http://www.imc.org/pdi/vcard-21.rtf">vCard 2.1</a>
144 * @see <a href="http://tools.ietf.org/html/rfc2426">RFC 2426 (3.0)</a>
145 * @see <a href="http://tools.ietf.org/html/rfc6350">RFC 6350 (4.0)</a>
146 */
147 public static ParserChainTextString parse(String str) {
148 return new ParserChainTextString(str);
149 }
150
151 /**
152 * <p>
153 * Parses plain text vCards.
154 * </p>
155 * <p>
156 * Use {@link VCardReader} for more control over the parsing.
157 * </p>
158 * @param file the vCard file
159 * @return chainer object for completing the parse operation
160 * @see VCardReader
161 * @see <a href="http://www.imc.org/pdi/vcard-21.rtf">vCard 2.1</a>
162 * @see <a href="http://tools.ietf.org/html/rfc2426">RFC 2426 (3.0)</a>
163 * @see <a href="http://tools.ietf.org/html/rfc6350">RFC 6350 (4.0)</a>
164 */
165 public static ParserChainTextReader parse(File file) {
166 return new ParserChainTextReader(file);
167 }
168
169 /**
170 * <p>
171 * Parses plain text vCards.
172 * </p>
173 * <p>
174 * Use {@link VCardReader} for more control over the parsing.
175 * </p>
176 * @param in the input stream
177 * @return chainer object for completing the parse operation
178 * @see VCardReader
179 * @see <a href="http://www.imc.org/pdi/vcard-21.rtf">vCard 2.1</a>
180 * @see <a href="http://tools.ietf.org/html/rfc2426">RFC 2426 (3.0)</a>
181 * @see <a href="http://tools.ietf.org/html/rfc6350">RFC 6350 (4.0)</a>
182 */
183 public static ParserChainTextReader parse(InputStream in) {
184 return parse(new InputStreamReader(in));
185 }
186
187 /**
188 * <p>
189 * Parses plain text vCards.
190 * </p>
191 * <p>
192 * Use {@link VCardReader} for more control over the parsing.
193 * </p>
194 * @param reader the reader
195 * @return chainer object for completing the parse operation
196 * @see VCardReader
197 * @see <a href="http://www.imc.org/pdi/vcard-21.rtf">vCard 2.1</a>
198 * @see <a href="http://tools.ietf.org/html/rfc2426">RFC 2426 (3.0)</a>
199 * @see <a href="http://tools.ietf.org/html/rfc6350">RFC 6350 (4.0)</a>
200 */
201 public static ParserChainTextReader parse(Reader reader) {
202 return new ParserChainTextReader(reader);
203 }
204
205 /**
206 * <p>
207 * Parses XML-encoded vCards (xCard) from a string.
208 * </p>
209 * <p>
210 * Use {@link XCardDocument} for more control over the parsing.
211 * </p>
212 * @param xml the XML document
213 * @return chainer object for completing the parse operation
214 * @see XCardDocument
215 * @see <a href="http://tools.ietf.org/html/rfc6351">RFC 6351</a>
216 */
217 public static ParserChainXmlString parseXml(String xml) {
218 return new ParserChainXmlString(xml);
219 }
220
221 /**
222 * <p>
223 * Parses XML-encoded vCards (xCard) from a file.
224 * </p>
225 * <p>
226 * Use {@link XCardDocument} for more control over the parsing.
227 * </p>
228 * @param file the XML file
229 * @return chainer object for completing the parse operation
230 * @see XCardDocument
231 * @see <a href="http://tools.ietf.org/html/rfc6351">RFC 6351</a>
232 */
233 public static ParserChainXmlReader parseXml(File file) {
234 return new ParserChainXmlReader(file);
235 }
236
237 /**
238 * <p>
239 * Parses XML-encoded vCards (xCard) from an input stream.
240 * </p>
241 * <p>
242 * Use {@link XCardDocument} for more control over the parsing.
243 * </p>
244 * @param in the input stream to the XML document
245 * @return chainer object for completing the parse operation
246 * @see XCardDocument
247 * @see <a href="http://tools.ietf.org/html/rfc6351">RFC 6351</a>
248 */
249 public static ParserChainXmlReader parseXml(InputStream in) {
250 return new ParserChainXmlReader(in);
251 }
252
253 /**
254 * <p>
255 * Parses XML-encoded vCards (xCard) from a reader.
256 * </p>
257 * <p>
258 * Note that use of this method is discouraged. It ignores the character
259 * encoding that is defined within the XML document itself, and should only
260 * be used if the encoding is undefined or if the encoding needs to be
261 * ignored for whatever reason. The {@link #parseXml(InputStream)} method
262 * should be used instead, since it takes the XML document's character
263 * encoding into account when parsing.
264 * </p>
265 * <p>
266 * Use {@link XCardDocument} for more control over the parsing.
267 * </p>
268 * @param reader the reader to the XML document
269 * @return chainer object for completing the parse operation
270 * @see XCardDocument
271 * @see <a href="http://tools.ietf.org/html/rfc6351">RFC 6351</a>
272 */
273 public static ParserChainXmlReader parseXml(Reader reader) {
274 return new ParserChainXmlReader(reader);
275 }
276
277 /**
278 * <p>
279 * Parses XML-encoded vCards (xCard).
280 * </p>
281 * <p>
282 * Use {@link XCardDocument} for more control over the parsing.
283 * </p>
284 * @param document the XML document
285 * @return chainer object for completing the parse operation
286 * @see XCardDocument
287 * @see <a href="http://tools.ietf.org/html/rfc6351">RFC 6351</a>
288 */
289 public static ParserChainXmlDom parseXml(Document document) {
290 return new ParserChainXmlDom(document);
291 }
292
293 /**
294 * <p>
295 * Parses HTML-encoded vCards (hCard).
296 * </p>
297 * <p>
298 * Use {@link HCardReader} for more control over the parsing.
299 * </p>
300 * @param html the HTML page
301 * @return chainer object for completing the parse operation
302 * @see HCardReader
303 * @see <a href="http://microformats.org/wiki/hcard">hCard 1.0</a>
304 */
305 public static ParserChainHtmlString parseHtml(String html) {
306 return new ParserChainHtmlString(html);
307 }
308
309 /**
310 * <p>
311 * Parses HTML-encoded vCards (hCard).
312 * </p>
313 * <p>
314 * Use {@link HCardReader} for more control over the parsing.
315 * </p>
316 * @param file the HTML file
317 * @return chainer object for completing the parse operation
318 * @see HCardReader
319 * @see <a href="http://microformats.org/wiki/hcard">hCard 1.0</a>
320 */
321 public static ParserChainHtmlReader parseHtml(File file) {
322 return new ParserChainHtmlReader(file);
323 }
324
325 /**
326 * <p>
327 * Parses HTML-encoded vCards (hCard).
328 * </p>
329 * <p>
330 * Use {@link HCardReader} for more control over the parsing.
331 * </p>
332 * @param in the input stream to the HTML page
333 * @return chainer object for completing the parse operation
334 * @see HCardReader
335 * @see <a href="http://microformats.org/wiki/hcard">hCard 1.0</a>
336 */
337 public static ParserChainHtmlReader parseHtml(InputStream in) {
338 return parseHtml(new InputStreamReader(in));
339 }
340
341 /**
342 * <p>
343 * Parses HTML-encoded vCards (hCard).
344 * </p>
345 * <p>
346 * Use {@link HCardReader} for more control over the parsing.
347 * </p>
348 * @param reader the reader to the HTML page
349 * @return chainer object for completing the parse operation
350 * @see HCardReader
351 * @see <a href="http://microformats.org/wiki/hcard">hCard 1.0</a>
352 */
353 public static ParserChainHtmlReader parseHtml(Reader reader) {
354 return new ParserChainHtmlReader(reader);
355 }
356
357 /**
358 * <p>
359 * Parses HTML-encoded vCards (hCard).
360 * </p>
361 * <p>
362 * Use {@link HCardReader} for more control over the parsing.
363 * </p>
364 * @param url the URL of the webpage
365 * @return chainer object for completing the parse operation
366 * @see HCardReader
367 * @see <a href="http://microformats.org/wiki/hcard">hCard 1.0</a>
368 */
369 public static ParserChainHtmlReader parseHtml(URL url) {
370 return new ParserChainHtmlReader(url);
371 }
372
373 /**
374 * <p>
375 * Parses JSON-encoded vCards (jCard).
376 * </p>
377 * <p>
378 * Use {@link JCardReader} for more control over the parsing.
379 * </p>
380 * @param json the JSON string
381 * @return chainer object for completing the parse operation
382 * @see JCardReader
383 * @see <a href="http://tools.ietf.org/html/rfc7095">RFC 7095</a>
384 */
385 public static ParserChainJsonString parseJson(String json) {
386 return new ParserChainJsonString(json);
387 }
388
389 /**
390 * <p>
391 * Parses JSON-encoded vCards (jCard).
392 * </p>
393 * <p>
394 * Use {@link JCardReader} for more control over the parsing.
395 * </p>
396 * @param file the JSON file
397 * @return chainer object for completing the parse operation
398 * @see JCardReader
399 * @see <a href="http://tools.ietf.org/html/rfc7095">RFC 7095</a>
400 */
401 public static ParserChainJsonReader parseJson(File file) {
402 return new ParserChainJsonReader(file);
403 }
404
405 /**
406 * <p>
407 * Parses JSON-encoded vCards (jCard).
408 * </p>
409 * <p>
410 * Use {@link JCardReader} for more control over the parsing.
411 * </p>
412 * @param in the input stream
413 * @return chainer object for completing the parse operation
414 * @see JCardReader
415 * @see <a href="http://tools.ietf.org/html/rfc7095">RFC 7095</a>
416 */
417 public static ParserChainJsonReader parseJson(InputStream in) {
418 return new ParserChainJsonReader(in);
419 }
420
421 /**
422 * <p>
423 * Parses JSON-encoded vCards (jCard).
424 * </p>
425 * <p>
426 * Use {@link JCardReader} for more control over the parsing.
427 * </p>
428 * @param reader the reader
429 * @return chainer object for completing the parse operation
430 * @see JCardReader
431 * @see <a href="http://tools.ietf.org/html/rfc7095">RFC 7095</a>
432 */
433 public static ParserChainJsonReader parseJson(Reader reader) {
434 return new ParserChainJsonReader(reader);
435 }
436
437 /**
438 * <p>
439 * Marshals one or more vCards to their traditional, plain-text
440 * representation.
441 * </p>
442 *
443 * <p>
444 * Use {@link VCardWriter} for more control over how the vCards are written.
445 * </p>
446 * @param vcards the vCards to marshal
447 * @return chainer object for completing the write operation
448 * @see VCardWriter
449 * @see <a href="http://www.imc.org/pdi/vcard-21.rtf">vCard 2.1</a>
450 * @see <a href="http://tools.ietf.org/html/rfc2426">RFC 2426 (3.0)</a>
451 * @see <a href="http://tools.ietf.org/html/rfc6350">RFC 6350 (4.0)</a>
452 */
453 public static WriterChainText write(VCard... vcards) {
454 return write(Arrays.asList(vcards));
455 }
456
457 /**
458 * <p>
459 * Marshals one or more vCards to their traditional, plain-text
460 * representation.
461 * </p>
462 *
463 * <p>
464 * Use {@link VCardWriter} for more control over how the vCards are written.
465 * </p>
466 * @param vcards the vCards to marshal
467 * @return chainer object for completing the write operation
468 * @see VCardWriter
469 * @see <a href="http://www.imc.org/pdi/vcard-21.rtf">vCard 2.1</a>
470 * @see <a href="http://tools.ietf.org/html/rfc2426">RFC 2426 (3.0)</a>
471 * @see <a href="http://tools.ietf.org/html/rfc6350">RFC 6350 (4.0)</a>
472 */
473 public static WriterChainText write(Collection<VCard> vcards) {
474 return new WriterChainText(vcards);
475 }
476
477 /**
478 * <p>
479 * Marshals one or more vCards to their XML representation (xCard).
480 * </p>
481 *
482 * <p>
483 * Use {@link XCardDocument} for more control over how the vCards are
484 * written.
485 * </p>
486 * @param vcards the vCards to marshal
487 * @return chainer object for completing the write operation
488 * @see XCardDocument
489 * @see <a href="http://tools.ietf.org/html/rfc6351">RFC 6351</a>
490 */
491 public static WriterChainXml writeXml(VCard... vcards) {
492 return writeXml(Arrays.asList(vcards));
493 }
494
495 /**
496 * <p>
497 * Marshals one or more vCards to their XML representation (xCard).
498 * </p>
499 *
500 * <p>
501 * Use {@link XCardDocument} for more control over how the vCards are
502 * written.
503 * </p>
504 * @param vcards the vCard to marshal
505 * @return chainer object for completing the write operation
506 * @see XCardDocument
507 * @see <a href="http://tools.ietf.org/html/rfc6351">RFC 6351</a>
508 */
509 public static WriterChainXml writeXml(Collection<VCard> vcards) {
510 return new WriterChainXml(vcards);
511 }
512
513 /**
514 * <p>
515 * Marshals one or more vCards their HTML representation (hCard).
516 * </p>
517 *
518 * <p>
519 * Use {@link HCardPage} for more control over how the vCards are written.
520 * </p>
521 * @param vcards the vCard(s) to marshal
522 * @return chainer object for completing the write operation
523 * @see HCardPage
524 * @see <a href="http://microformats.org/wiki/hcard">hCard 1.0</a>
525 */
526 public static WriterChainHtml writeHtml(VCard... vcards) {
527 return writeHtml(Arrays.asList(vcards));
528 }
529
530 /**
531 * <p>
532 * Marshals one or more vCards their HTML representation (hCard).
533 * </p>
534 *
535 * <p>
536 * Use {@link HCardPage} for more control over how the vCards are written.
537 * </p>
538 * @param vcards the vCard(s) to marshal
539 * @return chainer object for completing the write operation
540 * @see HCardPage
541 * @see <a href="http://microformats.org/wiki/hcard">hCard 1.0</a>
542 */
543 public static WriterChainHtml writeHtml(Collection<VCard> vcards) {
544 return new WriterChainHtml(vcards);
545 }
546
547 /**
548 * <p>
549 * Marshals one or more vCards to their JSON representation (jCard).
550 * </p>
551 *
552 * <p>
553 * Use {@link JCardWriter} for more control over how the vCards are written.
554 * </p>
555 * @param vcards the vCards to marshal
556 * @return chainer object for completing the write operation
557 * @see JCardWriter
558 * @see <a href="http://tools.ietf.org/html/rfc7095">RFC 7095</a>
559 */
560 public static WriterChainJson writeJson(VCard... vcards) {
561 return writeJson(Arrays.asList(vcards));
562 }
563
564 /**
565 * <p>
566 * Marshals one or more vCards to their JSON representation (jCard).
567 * </p>
568 *
569 * <p>
570 * Use {@link JCardWriter} for more control over how the vCards are written.
571 * </p>
572 * @param vcards the vCards to marshal
573 * @return chainer object for completing the write operation
574 * @see JCardWriter
575 * @see <a href="http://tools.ietf.org/html/rfc7095">RFC 7095</a>
576 */
577 public static WriterChainJson writeJson(Collection<VCard> vcards) {
578 return new WriterChainJson(vcards);
579 }
580
581 static abstract class ParserChain<T> {
582 final ScribeIndex index = new ScribeIndex();
583 List<List<String>> warnings;
584
585 @SuppressWarnings("unchecked")
586 final T this_ = (T) this;
587
588 /**
589 * Registers a property scribe.
590 * @param scribe the scribe
591 * @return this
592 */
593 public T register(VCardPropertyScribe<? extends VCardProperty> scribe) {
594 index.register(scribe);
595 return this_;
596 }
597
598 /**
599 * Provides a list object that any unmarshal warnings will be put into.
600 * @param warnings the list object that will be populated with the
601 * warnings of each unmarshalled vCard. Each element of the list is the
602 * list of warnings for one of the unmarshalled vCards. Therefore, the
603 * size of this list will be equal to the number of parsed vCards. If a
604 * vCard does not have any warnings, then its warning list will be
605 * empty.
606 * @return this
607 */
608 public T warnings(List<List<String>> warnings) {
609 this.warnings = warnings;
610 return this_;
611 }
612
613 /**
614 * Reads the first vCard from the stream.
615 * @return the vCard or null if there are no vCards
616 * @throws IOException if there's an I/O problem
617 * @throws SAXException if there's a problem parsing the XML
618 */
619 public abstract VCard first() throws IOException, SAXException;
620
621 /**
622 * Reads all vCards from the stream.
623 * @return the parsed vCards
624 * @throws IOException if there's an I/O problem
625 * @throws SAXException if there's a problem parsing the XML
626 */
627 public abstract List<VCard> all() throws IOException, SAXException;
628 }
629
630 static abstract class ParserChainText<T> extends ParserChain<T> {
631 boolean caretDecoding = true;
632 Charset defaultQuotedPrintableCharset;
633 final boolean closeWhenDone;
634
635 private ParserChainText(boolean closeWhenDone) {
636 this.closeWhenDone = closeWhenDone;
637 }
638
639 /**
640 * Sets whether the reader will decode characters in parameter values
641 * that use circumflex accent encoding (enabled by default).
642 *
643 * @param enable true to use circumflex accent decoding, false not to
644 * @return this
645 * @see VCardReader#setCaretDecodingEnabled(boolean)
646 * @see <a href="http://tools.ietf.org/html/rfc6868">RFC 6868</a>
647 */
648 public T caretDecoding(boolean enable) {
649 caretDecoding = enable;
650 return this_;
651 }
652
653 /**
654 * <p>
655 * Sets the character set to use when decoding quoted-printable values
656 * if the property has no CHARSET parameter, or if the CHARSET parameter
657 * is not a valid character set.
658 * </p>
659 * <p>
660 * By default, the Reader's character encoding will be used. If the
661 * Reader has no character encoding, then the system's default character
662 * encoding will be used.
663 * </p>
664 * @param charset the character set
665 * @return this
666 */
667 public T defaultQuotedPrintableCharset(Charset charset) {
668 defaultQuotedPrintableCharset = charset;
669 return this_;
670 }
671
672 @Override
673 public VCard first() throws IOException {
674 VCardReader parser = constructReader();
675
676 try {
677 VCard vcard = parser.readNext();
678 if (warnings != null) {
679 warnings.add(parser.getWarnings());
680 }
681 return vcard;
682 } finally {
683 if (closeWhenDone) {
684 IOUtils.closeQuietly(parser);
685 }
686 }
687 }
688
689 @Override
690 public List<VCard> all() throws IOException {
691 VCardReader parser = constructReader();
692
693 try {
694 List<VCard> vcards = new ArrayList<VCard>();
695 VCard vcard;
696 while ((vcard = parser.readNext()) != null) {
697 if (warnings != null) {
698 warnings.add(parser.getWarnings());
699 }
700 vcards.add(vcard);
701 }
702 return vcards;
703 } finally {
704 if (closeWhenDone) {
705 IOUtils.closeQuietly(parser);
706 }
707 }
708 }
709
710 private VCardReader constructReader() throws IOException {
711 VCardReader parser = _constructReader();
712 parser.setScribeIndex(index);
713 parser.setCaretDecodingEnabled(caretDecoding);
714 if (defaultQuotedPrintableCharset != null) {
715 parser.setDefaultQuotedPrintableCharset(defaultQuotedPrintableCharset);
716 }
717 return parser;
718 }
719
720 abstract VCardReader _constructReader() throws IOException;
721 }
722
723 /**
724 * Chainer class for parsing plain text vCards.
725 * @see Ezvcard#parse(InputStream)
726 * @see Ezvcard#parse(File)
727 * @see Ezvcard#parse(Reader)
728 */
729 public static class ParserChainTextReader extends ParserChainText<ParserChainTextReader> {
730 private final Reader reader;
731 private final File file;
732
733 private ParserChainTextReader(Reader reader) {
734 super(false);
735 this.reader = reader;
736 this.file = null;
737 }
738
739 private ParserChainTextReader(File file) {
740 super(true);
741 this.reader = null;
742 this.file = file;
743 }
744
745 @Override
746 public ParserChainTextReader register(VCardPropertyScribe<? extends VCardProperty> scribe) {
747 return super.register(scribe);
748 }
749
750 @Override
751 public ParserChainTextReader warnings(List<List<String>> warnings) {
752 return super.warnings(warnings);
753 }
754
755 @Override
756 public ParserChainTextReader caretDecoding(boolean enable) {
757 return super.caretDecoding(enable);
758 }
759
760 @Override
761 @SuppressWarnings("resource")
762 VCardReader _constructReader() throws IOException {
763 return (reader != null) ? new VCardReader(reader) : new VCardReader(file);
764 }
765 }
766
767 /**
768 * Chainer class for parsing plain text vCards.
769 * @see Ezvcard#parse(String)
770 */
771 public static class ParserChainTextString extends ParserChainText<ParserChainTextString> {
772 private final String text;
773
774 private ParserChainTextString(String text) {
775 super(false);
776 this.text = text;
777 }
778
779 @Override
780 public ParserChainTextString register(VCardPropertyScribe<? extends VCardProperty> scribe) {
781 return super.register(scribe);
782 }
783
784 @Override
785 public ParserChainTextString warnings(List<List<String>> warnings) {
786 return super.warnings(warnings);
787 }
788
789 @Override
790 public ParserChainTextString caretDecoding(boolean enable) {
791 return super.caretDecoding(enable);
792 }
793
794 @Override
795 VCardReader _constructReader() {
796 return new VCardReader(text);
797 }
798
799 @Override
800 public VCard first() {
801 try {
802 return super.first();
803 } catch (IOException e) {
804 //should never be thrown because we're reading from a string
805 throw new RuntimeException(e);
806 }
807 }
808
809 @Override
810 public List<VCard> all() {
811 try {
812 return super.all();
813 } catch (IOException e) {
814 //should never be thrown because we're reading from a string
815 throw new RuntimeException(e);
816 }
817 }
818 }
819
820 static abstract class ParserChainXml<T> extends ParserChain<T> {
821 @Override
822 public VCard first() throws IOException, SAXException {
823 XCardDocument document = constructDocument();
824 VCard vcard = document.parseFirst();
825 if (warnings != null) {
826 warnings.addAll(document.getParseWarnings());
827 }
828 return vcard;
829 }
830
831 @Override
832 public List<VCard> all() throws IOException, SAXException {
833 XCardDocument document = constructDocument();
834 List<VCard> icals = document.parseAll();
835 if (warnings != null) {
836 warnings.addAll(document.getParseWarnings());
837 }
838 return icals;
839 }
840
841 private XCardDocument constructDocument() throws SAXException, IOException {
842 XCardDocument parser = _constructDocument();
843 parser.setScribeIndex(index);
844 return parser;
845 }
846
847 abstract XCardDocument _constructDocument() throws IOException, SAXException;
848 }
849
850 /**
851 * Chainer class for parsing XML vCards.
852 * @see Ezvcard#parseXml(InputStream)
853 * @see Ezvcard#parseXml(File)
854 * @see Ezvcard#parseXml(Reader)
855 */
856 public static class ParserChainXmlReader extends ParserChainXml<ParserChainXmlReader> {
857 private final InputStream in;
858 private final File file;
859 private final Reader reader;
860
861 private ParserChainXmlReader(InputStream in) {
862 this.in = in;
863 this.reader = null;
864 this.file = null;
865 }
866
867 private ParserChainXmlReader(File file) {
868 this.in = null;
869 this.reader = null;
870 this.file = file;
871 }
872
873 private ParserChainXmlReader(Reader reader) {
874 this.in = null;
875 this.reader = reader;
876 this.file = null;
877 }
878
879 @Override
880 public ParserChainXmlReader register(VCardPropertyScribe<? extends VCardProperty> scribe) {
881 return super.register(scribe);
882 }
883
884 @Override
885 public ParserChainXmlReader warnings(List<List<String>> warnings) {
886 return super.warnings(warnings);
887 }
888
889 @Override
890 XCardDocument _constructDocument() throws IOException, SAXException {
891 if (in != null) {
892 return new XCardDocument(in);
893 }
894 if (file != null) {
895 return new XCardDocument(file);
896 }
897 return new XCardDocument(reader);
898 }
899 }
900
901 /**
902 * Chainer class for parsing XML vCards.
903 * @see Ezvcard#parseXml(String)
904 */
905 public static class ParserChainXmlString extends ParserChainXml<ParserChainXmlString> {
906 private final String xml;
907
908 private ParserChainXmlString(String xml) {
909 this.xml = xml;
910 }
911
912 @Override
913 public ParserChainXmlString register(VCardPropertyScribe<? extends VCardProperty> scribe) {
914 return super.register(scribe);
915 }
916
917 @Override
918 public ParserChainXmlString warnings(List<List<String>> warnings) {
919 return super.warnings(warnings);
920 }
921
922 @Override
923 XCardDocument _constructDocument() throws SAXException {
924 return new XCardDocument(xml);
925 }
926
927 @Override
928 public VCard first() throws SAXException {
929 try {
930 return super.first();
931 } catch (IOException e) {
932 //should never be thrown because we're reading from a string
933 throw new RuntimeException(e);
934 }
935 }
936
937 @Override
938 public List<VCard> all() throws SAXException {
939 try {
940 return super.all();
941 } catch (IOException e) {
942 //should never be thrown because we're reading from a string
943 throw new RuntimeException(e);
944 }
945 }
946 }
947
948 /**
949 * Chainer class for parsing XML vCards.
950 * @see Ezvcard#parseXml(Document)
951 */
952 public static class ParserChainXmlDom extends ParserChainXml<ParserChainXmlDom> {
953 private final Document document;
954
955 private ParserChainXmlDom(Document document) {
956 this.document = document;
957 }
958
959 @Override
960 public ParserChainXmlDom register(VCardPropertyScribe<? extends VCardProperty> scribe) {
961 return super.register(scribe);
962 }
963
964 @Override
965 public ParserChainXmlDom warnings(List<List<String>> warnings) {
966 return super.warnings(warnings);
967 }
968
969 @Override
970 XCardDocument _constructDocument() {
971 return new XCardDocument(document);
972 }
973
974 @Override
975 public VCard first() {
976 try {
977 return super.first();
978 } catch (IOException e) {
979 //should never be thrown because we're reading from a DOM
980 throw new RuntimeException(e);
981 } catch (SAXException e) {
982 //should never be thrown because we're reading from a DOM
983 throw new RuntimeException(e);
984 }
985 }
986
987 @Override
988 public List<VCard> all() {
989 try {
990 return super.all();
991 } catch (IOException e) {
992 //should never be thrown because we're reading from a DOM
993 throw new RuntimeException(e);
994 } catch (SAXException e) {
995 //should never be thrown because we're reading from a DOM
996 throw new RuntimeException(e);
997 }
998 }
999 }
1000
1001 static abstract class ParserChainHtml<T> extends ParserChain<T> {
1002 String pageUrl;
1003
1004 /**
1005 * Sets the original URL of the webpage. This is used to resolve
1006 * relative links and to set the SOURCE property on the vCard. Setting
1007 * this property has no effect if reading from a {@link URL}.
1008 * @param pageUrl the webpage URL
1009 * @return this
1010 */
1011 public T pageUrl(String pageUrl) {
1012 this.pageUrl = pageUrl;
1013 return this_;
1014 }
1015
1016 @Override
1017 public VCard first() throws IOException {
1018 HCardReader parser = constructReader();
1019
1020 VCard vcard = parser.readNext();
1021 if (warnings != null) {
1022 warnings.add(parser.getWarnings());
1023 }
1024 return vcard;
1025 }
1026
1027 @Override
1028 public List<VCard> all() throws IOException {
1029 HCardReader parser = constructReader();
1030
1031 List<VCard> vcards = new ArrayList<VCard>();
1032 VCard vcard;
1033 while ((vcard = parser.readNext()) != null) {
1034 if (warnings != null) {
1035 warnings.add(parser.getWarnings());
1036 }
1037 vcards.add(vcard);
1038 }
1039 return vcards;
1040 }
1041
1042 private HCardReader constructReader() throws IOException {
1043 HCardReader parser = _constructReader();
1044 parser.setScribeIndex(index);
1045 return parser;
1046 }
1047
1048 abstract HCardReader _constructReader() throws IOException;
1049 }
1050
1051 /**
1052 * Chainer class for parsing HTML vCards.
1053 * @see Ezvcard#parseHtml(InputStream)
1054 * @see Ezvcard#parseHtml(File)
1055 * @see Ezvcard#parseHtml(Reader)
1056 */
1057 public static class ParserChainHtmlReader extends ParserChainHtml<ParserChainHtmlReader> {
1058 private final Reader reader;
1059 private final File file;
1060 private final URL url;
1061
1062 private ParserChainHtmlReader(Reader reader) {
1063 this.reader = reader;
1064 this.file = null;
1065 this.url = null;
1066 }
1067
1068 private ParserChainHtmlReader(File file) {
1069 this.reader = null;
1070 this.file = file;
1071 this.url = null;
1072 }
1073
1074 private ParserChainHtmlReader(URL url) {
1075 this.reader = null;
1076 this.file = null;
1077 this.url = url;
1078 }
1079
1080 @Override
1081 public ParserChainHtmlReader register(VCardPropertyScribe<? extends VCardProperty> scribe) {
1082 return super.register(scribe);
1083 }
1084
1085 @Override
1086 public ParserChainHtmlReader warnings(List<List<String>> warnings) {
1087 return super.warnings(warnings);
1088 }
1089
1090 @Override
1091 public ParserChainHtmlReader pageUrl(String pageUrl) {
1092 return super.pageUrl(pageUrl);
1093 }
1094
1095 @Override
1096 HCardReader _constructReader() throws IOException {
1097 if (reader != null) {
1098 return new HCardReader(reader, pageUrl);
1099 }
1100
1101 if (file != null) {
1102 //Jsoup (presumably) closes the FileReader it creates
1103 return new HCardReader(file, pageUrl);
1104 }
1105
1106 return new HCardReader(url);
1107 }
1108 }
1109
1110 /**
1111 * Chainer class for parsing HTML vCards.
1112 * @see Ezvcard#parseHtml(String)
1113 */
1114 public static class ParserChainHtmlString extends ParserChainHtml<ParserChainHtmlString> {
1115 private final String html;
1116
1117 private ParserChainHtmlString(String html) {
1118 this.html = html;
1119 }
1120
1121 @Override
1122 public ParserChainHtmlString register(VCardPropertyScribe<? extends VCardProperty> scribe) {
1123 return super.register(scribe);
1124 }
1125
1126 @Override
1127 public ParserChainHtmlString warnings(List<List<String>> warnings) {
1128 return super.warnings(warnings);
1129 }
1130
1131 @Override
1132 public ParserChainHtmlString pageUrl(String pageUrl) {
1133 return super.pageUrl(pageUrl);
1134 }
1135
1136 @Override
1137 HCardReader _constructReader() {
1138 return new HCardReader(html, pageUrl);
1139 }
1140
1141 @Override
1142 public VCard first() {
1143 try {
1144 return super.first();
1145 } catch (IOException e) {
1146 //should never be thrown because we're reading from a string
1147 throw new RuntimeException(e);
1148 }
1149 }
1150
1151 @Override
1152 public List<VCard> all() {
1153 try {
1154 return super.all();
1155 } catch (IOException e) {
1156 //should never be thrown because we're reading from a string
1157 throw new RuntimeException(e);
1158 }
1159 }
1160 }
1161
1162 static abstract class ParserChainJson<T> extends ParserChain<T> {
1163 final boolean closeWhenDone;
1164
1165 private ParserChainJson(boolean closeWhenDone) {
1166 this.closeWhenDone = closeWhenDone;
1167 }
1168
1169 /**
1170 * @throws JCardParseException if the jCard syntax is incorrect (the
1171 * JSON syntax may be valid, but it is not in the correct jCard format).
1172 * @throws JsonParseException if the JSON syntax is incorrect
1173 */
1174 @Override
1175 public VCard first() throws IOException {
1176 JCardReader parser = constructReader();
1177
1178 try {
1179 VCard vcard = parser.readNext();
1180 if (warnings != null) {
1181 warnings.add(parser.getWarnings());
1182 }
1183 return vcard;
1184 } finally {
1185 if (closeWhenDone) {
1186 IOUtils.closeQuietly(parser);
1187 }
1188 }
1189 }
1190
1191 /**
1192 * @throws JCardParseException if the jCard syntax is incorrect (the
1193 * JSON syntax may be valid, but it is not in the correct jCard format).
1194 * @throws JsonParseException if the JSON syntax is incorrect
1195 */
1196 @Override
1197 public List<VCard> all() throws IOException {
1198 JCardReader parser = constructReader();
1199
1200 try {
1201 List<VCard> vcards = new ArrayList<VCard>();
1202 VCard vcard;
1203 while ((vcard = parser.readNext()) != null) {
1204 if (warnings != null) {
1205 warnings.add(parser.getWarnings());
1206 }
1207 vcards.add(vcard);
1208 }
1209 return vcards;
1210 } finally {
1211 if (closeWhenDone) {
1212 IOUtils.closeQuietly(parser);
1213 }
1214 }
1215 }
1216
1217 private JCardReader constructReader() throws IOException {
1218 JCardReader parser = _constructReader();
1219 parser.setScribeIndex(index);
1220 return parser;
1221 }
1222
1223 abstract JCardReader _constructReader() throws IOException;
1224 }
1225
1226 /**
1227 * Chainer class for parsing JSON-encoded vCards (jCard).
1228 * @see Ezvcard#parseJson(InputStream)
1229 * @see Ezvcard#parseJson(File)
1230 * @see Ezvcard#parseJson(Reader)
1231 */
1232 public static class ParserChainJsonReader extends ParserChainJson<ParserChainJsonReader> {
1233 private final InputStream in;
1234 private final File file;
1235 private final Reader reader;
1236
1237 private ParserChainJsonReader(InputStream in) {
1238 super(false);
1239 this.in = in;
1240 this.reader = null;
1241 this.file = null;
1242 }
1243
1244 private ParserChainJsonReader(File file) {
1245 super(true);
1246 this.in = null;
1247 this.reader = null;
1248 this.file = file;
1249 }
1250
1251 private ParserChainJsonReader(Reader reader) {
1252 super(false);
1253 this.in = null;
1254 this.reader = reader;
1255 this.file = null;
1256 }
1257
1258 @Override
1259 public ParserChainJsonReader register(VCardPropertyScribe<? extends VCardProperty> scribe) {
1260 return super.register(scribe);
1261 }
1262
1263 @Override
1264 public ParserChainJsonReader warnings(List<List<String>> warnings) {
1265 return super.warnings(warnings);
1266 }
1267
1268 @Override
1269 JCardReader _constructReader() throws IOException {
1270 if (in != null) {
1271 return new JCardReader(in);
1272 }
1273 if (file != null) {
1274 return new JCardReader(file);
1275 }
1276 return new JCardReader(reader);
1277 }
1278 }
1279
1280 /**
1281 * Chainer class for parsing JSON-encoded vCards (jCard).
1282 * @see Ezvcard#parseJson(String)
1283 */
1284 public static class ParserChainJsonString extends ParserChainJson<ParserChainJsonString> {
1285 private final String json;
1286
1287 private ParserChainJsonString(String json) {
1288 super(false);
1289 this.json = json;
1290 }
1291
1292 @Override
1293 public ParserChainJsonString register(VCardPropertyScribe<? extends VCardProperty> scribe) {
1294 return super.register(scribe);
1295 }
1296
1297 @Override
1298 public ParserChainJsonString warnings(List<List<String>> warnings) {
1299 return super.warnings(warnings);
1300 }
1301
1302 @Override
1303 JCardReader _constructReader() {
1304 return new JCardReader(json);
1305 }
1306
1307 @Override
1308 public VCard first() {
1309 try {
1310 return super.first();
1311 } catch (IOException e) {
1312 //should never be thrown because we're reading from a string
1313 throw new RuntimeException(e);
1314 }
1315 }
1316
1317 @Override
1318 public List<VCard> all() {
1319 try {
1320 return super.all();
1321 } catch (IOException e) {
1322 //should never be thrown because we're reading from a string
1323 throw new RuntimeException(e);
1324 }
1325 }
1326 }
1327
1328 static abstract class WriterChain<T> {
1329 final Collection<VCard> vcards;
1330
1331 @SuppressWarnings("unchecked")
1332 final T this_ = (T) this;
1333
1334 WriterChain(Collection<VCard> vcards) {
1335 this.vcards = vcards;
1336 }
1337 }
1338
1339 /**
1340 * Chainer class for writing plain text vCards
1341 * @see Ezvcard#write(Collection)
1342 * @see Ezvcard#write(VCard...)
1343 */
1344 public static class WriterChainText extends WriterChain<WriterChainText> {
1345 VCardVersion version;
1346 boolean prodId = true;
1347 boolean versionStrict = true;
1348 boolean caretEncoding = false;
1349 final ScribeIndex index = new ScribeIndex();
1350
1351 private WriterChainText(Collection<VCard> vcards) {
1352 super(vcards);
1353 }
1354
1355 /**
1356 * <p>
1357 * Sets the version that all the vCards will be marshalled to. The
1358 * version that is attached to each individual {@link VCard} object will
1359 * be ignored.
1360 * </p>
1361 * <p>
1362 * If no version is passed into this method, the writer will look at the
1363 * version attached to each individual {@link VCard} object and marshal
1364 * it to that version. And if a {@link VCard} object has no version
1365 * attached to it, then it will be marshalled to version 3.0.
1366 * </p>
1367 * @param version the version to marshal the vCards to
1368 * @return this
1369 */
1370 public WriterChainText version(VCardVersion version) {
1371 this.version = version;
1372 return this_;
1373 }
1374
1375 /**
1376 * Sets whether or not to add a PRODID property to each vCard, saying
1377 * that the vCard was generated by this library. For 2.1 vCards, the
1378 * extended property X-PRODID is used, since PRODID is not supported by
1379 * that version.
1380 * @param include true to add PRODID (default), false not to
1381 * @return this
1382 */
1383 public WriterChainText prodId(boolean include) {
1384 this.prodId = include;
1385 return this_;
1386 }
1387
1388 /**
1389 * Sets whether the writer will use circumflex accent encoding for vCard
1390 * 3.0 and 4.0 parameter values (disabled by default).
1391 * @param enable true to use circumflex accent encoding, false not to
1392 * @return this
1393 * @see VCardWriter#setCaretEncodingEnabled(boolean)
1394 * @see <a href="http://tools.ietf.org/html/rfc6868">RFC 6868</a>
1395 */
1396 public WriterChainText caretEncoding(boolean enable) {
1397 this.caretEncoding = enable;
1398 return this_;
1399 }
1400
1401 /**
1402 * Sets whether properties that do not support the target version will
1403 * be excluded from the written vCard.
1404 * @param versionStrict true to exclude properties that do not support
1405 * the target version, false to include them anyway (defaults to true)
1406 * @return this
1407 */
1408 public WriterChainText versionStrict(boolean versionStrict) {
1409 this.versionStrict = versionStrict;
1410 return this_;
1411 }
1412
1413 /**
1414 * Registers a property scribe.
1415 * @param scribe the scribe to register
1416 * @return this
1417 */
1418 public WriterChainText register(VCardPropertyScribe<? extends VCardProperty> scribe) {
1419 index.register(scribe);
1420 return this_;
1421 }
1422
1423 /**
1424 * Writes the vCards to a string.
1425 * @return the vCard string
1426 */
1427 public String go() {
1428 StringWriter sw = new StringWriter();
1429 try {
1430 go(sw);
1431 } catch (IOException e) {
1432 //writing to a string
1433 }
1434 return sw.toString();
1435 }
1436
1437 /**
1438 * Writes the vCards to an output stream.
1439 * @param out the output stream to write to
1440 * @throws IOException if there's a problem writing to the output stream
1441 */
1442 public void go(OutputStream out) throws IOException {
1443 VCardWriter vcardWriter = (version == null) ? new VCardWriter(out) : new VCardWriter(out, version);
1444 go(vcardWriter);
1445 }
1446
1447 /**
1448 * Writes the vCards to a file. If the file exists, it will be
1449 * overwritten.
1450 * @param file the file to write to
1451 * @throws IOException if there's a problem writing to the file
1452 */
1453 public void go(File file) throws IOException {
1454 go(file, false);
1455 }
1456
1457 /**
1458 * Writes the vCards to a file.
1459 * @param file the file to write to
1460 * @param append true to append onto the end of the file, false to
1461 * overwrite it
1462 * @throws IOException if there's a problem writing to the file
1463 */
1464 public void go(File file, boolean append) throws IOException {
1465 VCardWriter vcardWriter = (version == null) ? new VCardWriter(file, append) : new VCardWriter(file, append, version);
1466 try {
1467 go(vcardWriter);
1468 } finally {
1469 IOUtils.closeQuietly(vcardWriter);
1470 }
1471 }
1472
1473 /**
1474 * Writes the vCards to a writer.
1475 * @param writer the writer to write to
1476 * @throws IOException if there's a problem writing to the writer
1477 */
1478 public void go(Writer writer) throws IOException {
1479 VCardWriter vcardWriter = new VCardWriter(writer);
1480 vcardWriter.setTargetVersion(version);
1481 go(vcardWriter);
1482 }
1483
1484 private void go(VCardWriter vcardWriter) throws IOException {
1485 vcardWriter.setAddProdId(prodId);
1486 vcardWriter.setCaretEncodingEnabled(caretEncoding);
1487 vcardWriter.setVersionStrict(versionStrict);
1488 vcardWriter.setScribeIndex(index);
1489
1490 for (VCard vcard : vcards) {
1491 if (version == null) {
1492 VCardVersion vcardVersion = vcard.getVersion();
1493 if (vcardVersion == null) {
1494 vcardVersion = VCardVersion.V3_0;
1495 }
1496 vcardWriter.setTargetVersion(vcardVersion);
1497 }
1498 vcardWriter.write(vcard);
1499 vcardWriter.flush();
1500 }
1501 }
1502 }
1503
1504 /**
1505 * Chainer class for writing XML vCards (xCard).
1506 * @see Ezvcard#writeXml(Collection)
1507 * @see Ezvcard#writeXml(VCard...)
1508 */
1509 public static class WriterChainXml extends WriterChain<WriterChainXml> {
1510 boolean prodId = true;
1511 boolean versionStrict = true;
1512 int indent = -1;
1513 final ScribeIndex index = new ScribeIndex();
1514
1515 private WriterChainXml(Collection<VCard> vcards) {
1516 super(vcards);
1517 }
1518
1519 /**
1520 * Sets whether or not to add a PRODID property to each vCard, saying
1521 * that the vCard was generated by this library.
1522 * @param include true to add PRODID (default), false not to
1523 * @return this
1524 */
1525 public WriterChainXml prodId(boolean include) {
1526 this.prodId = include;
1527 return this_;
1528 }
1529
1530 /**
1531 * Sets the number of indent spaces to use for pretty-printing. If not
1532 * set, then the XML will not be pretty-printed.
1533 * @param indent the number of spaces in the indent string
1534 * @return this
1535 */
1536 public WriterChainXml indent(int indent) {
1537 this.indent = indent;
1538 return this_;
1539 }
1540
1541 /**
1542 * Sets whether properties that do not support xCard (vCard version 4.0)
1543 * will be excluded from the written vCard.
1544 * @param versionStrict true to exclude properties that do not support
1545 * xCard, false to include them anyway (defaults to true)
1546 * @return this
1547 */
1548 public WriterChainXml versionStrict(boolean versionStrict) {
1549 this.versionStrict = versionStrict;
1550 return this_;
1551 }
1552
1553 /**
1554 * Registers a property scribe.
1555 * @param scribe the scribe to register
1556 * @return this
1557 */
1558 public WriterChainXml register(VCardPropertyScribe<? extends VCardProperty> scribe) {
1559 index.register(scribe);
1560 return this_;
1561 }
1562
1563 /**
1564 * Writes the xCards to a string.
1565 * @return the XML document
1566 */
1567 public String go() {
1568 StringWriter sw = new StringWriter();
1569 try {
1570 go(sw);
1571 } catch (TransformerException e) {
1572 //writing to a string
1573 }
1574 return sw.toString();
1575 }
1576
1577 /**
1578 * Writes the xCards to an output stream.
1579 * @param out the output stream to write to
1580 * @throws TransformerException if there's a problem writing to the
1581 * output stream
1582 */
1583 public void go(OutputStream out) throws TransformerException {
1584 XCardDocument doc = createXCardDocument();
1585 doc.write(out, indent);
1586 }
1587
1588 /**
1589 * Writes the xCards to a file.
1590 * @param file the file to write to
1591 * @throws IOException if the file can't be opened
1592 * @throws TransformerException if there's a problem writing to the file
1593 */
1594 public void go(File file) throws IOException, TransformerException {
1595 XCardDocument doc = createXCardDocument();
1596 doc.write(file, indent);
1597 }
1598
1599 /**
1600 * Writes the xCards to a writer.
1601 * @param writer the writer to write to
1602 * @throws TransformerException if there's a problem writing to the
1603 * writer
1604 */
1605 public void go(Writer writer) throws TransformerException {
1606 XCardDocument doc = createXCardDocument();
1607 doc.write(writer, indent);
1608 }
1609
1610 /**
1611 * Generates an XML document object model (DOM) containing the xCards.
1612 * @return the DOM
1613 */
1614 public Document dom() {
1615 XCardDocument doc = createXCardDocument();
1616 return doc.getDocument();
1617 }
1618
1619 private XCardDocument createXCardDocument() {
1620 XCardDocument doc = new XCardDocument();
1621 doc.setAddProdId(prodId);
1622 doc.setVersionStrict(versionStrict);
1623 doc.setScribeIndex(index);
1624
1625 for (VCard vcard : vcards) {
1626 doc.add(vcard);
1627 }
1628
1629 return doc;
1630 }
1631 }
1632
1633 /**
1634 * Chainer class for writing HTML vCards (hCard).
1635 * @see Ezvcard#writeHtml(Collection)
1636 * @see Ezvcard#writeHtml(VCard...)
1637 */
1638 public static class WriterChainHtml extends WriterChain<WriterChainHtml> {
1639 private WriterChainHtml(Collection<VCard> vcards) {
1640 super(vcards);
1641 }
1642
1643 /**
1644 * Writes the hCards to a string.
1645 * @return the HTML page
1646 */
1647 public String go() {
1648 StringWriter sw = new StringWriter();
1649 try {
1650 go(sw);
1651 } catch (IOException e) {
1652 //writing string
1653 }
1654 return sw.toString();
1655 }
1656
1657 /**
1658 * Writes the hCards to an output stream.
1659 * @param out the output stream to write to
1660 * @throws IOException if there's a problem writing to the output stream
1661 */
1662 public void go(OutputStream out) throws IOException {
1663 go(new OutputStreamWriter(out));
1664 }
1665
1666 /**
1667 * Writes the hCards to a file.
1668 * @param file the file to write to
1669 * @throws IOException if there's a problem writing to the file
1670 */
1671 public void go(File file) throws IOException {
1672 FileWriter writer = null;
1673 try {
1674 writer = new FileWriter(file);
1675 go(writer);
1676 } finally {
1677 IOUtils.closeQuietly(writer);
1678 }
1679 }
1680
1681 /**
1682 * Writes the hCards to a writer.
1683 * @param writer the writer to write to
1684 * @throws IOException if there's a problem writing to the writer
1685 */
1686 public void go(Writer writer) throws IOException {
1687 HCardPage page = new HCardPage();
1688 for (VCard vcard : vcards) {
1689 page.add(vcard);
1690 }
1691 page.write(writer);
1692 }
1693 }
1694
1695 /**
1696 * Chainer class for writing JSON-encoded vCards (jCard).
1697 * @see Ezvcard#writeJson(Collection)
1698 * @see Ezvcard#writeJson(VCard...)
1699 */
1700 public static class WriterChainJson extends WriterChain<WriterChainJson> {
1701 boolean prodId = true;
1702 boolean versionStrict = true;
1703 boolean indent = false;
1704 final ScribeIndex index = new ScribeIndex();
1705
1706 private WriterChainJson(Collection<VCard> vcards) {
1707 super(vcards);
1708 }
1709
1710 /**
1711 * Sets whether or not to add a PRODID property to each vCard, saying
1712 * that the vCard was generated by this library.
1713 * @param include true to add PRODID (default), false not to
1714 * @return this
1715 */
1716 public WriterChainJson prodId(boolean include) {
1717 this.prodId = include;
1718 return this_;
1719 }
1720
1721 /**
1722 * Sets whether or not to pretty-print the JSON.
1723 * @param indent true to pretty-print it, false not to (defaults to
1724 * false)
1725 * @return this
1726 */
1727 public WriterChainJson indent(boolean indent) {
1728 this.indent = indent;
1729 return this_;
1730 }
1731
1732 /**
1733 * Sets whether properties that do not support jCard (vCard version 4.0)
1734 * will be excluded from the written vCard.
1735 * @param versionStrict true to exclude properties that do not support
1736 * jCard, false to include them anyway (defaults to true)
1737 * @return this
1738 */
1739 public WriterChainJson versionStrict(boolean versionStrict) {
1740 this.versionStrict = versionStrict;
1741 return this_;
1742 }
1743
1744 /**
1745 * Registers a property scribe.
1746 * @param scribe the scribe to register
1747 * @return this
1748 */
1749 public WriterChainJson register(VCardPropertyScribe<? extends VCardProperty> scribe) {
1750 index.register(scribe);
1751 return this_;
1752 }
1753
1754 /**
1755 * Writes the jCards to a string.
1756 * @return the JSON string
1757 */
1758 public String go() {
1759 StringWriter sw = new StringWriter();
1760 try {
1761 go(sw);
1762 } catch (IOException e) {
1763 //writing to a string
1764 }
1765 return sw.toString();
1766 }
1767
1768 /**
1769 * Writes the jCards to an output stream.
1770 * @param out the output stream to write to
1771 * @throws IOException if there's a problem writing to the output stream
1772 */
1773 public void go(OutputStream out) throws IOException {
1774 go(new JCardWriter(out, vcards.size() > 1));
1775 }
1776
1777 /**
1778 * Writes the jCards to a file.
1779 * @param file the file to write to
1780 * @throws IOException if there's a problem writing to the file
1781 */
1782 public void go(File file) throws IOException {
1783 JCardWriter writer = new JCardWriter(file, vcards.size() > 1);
1784 try {
1785 go(writer);
1786 } finally {
1787 IOUtils.closeQuietly(writer);
1788 }
1789 }
1790
1791 /**
1792 * Writes the jCards to a writer.
1793 * @param writer the writer to write to
1794 * @throws IOException if there's a problem writing to the writer
1795 */
1796 public void go(Writer writer) throws IOException {
1797 go(new JCardWriter(writer, vcards.size() > 1));
1798 }
1799
1800 private void go(JCardWriter writer) throws IOException {
1801 writer.setAddProdId(prodId);
1802 writer.setIndent(indent);
1803 writer.setVersionStrict(versionStrict);
1804 writer.setScribeIndex(index);
1805 try {
1806 for (VCard vcard : vcards) {
1807 writer.write(vcard);
1808 writer.flush();
1809 }
1810 } finally {
1811 writer.closeJsonStream();
1812 }
1813 }
1814 }
1815
1816 private Ezvcard() {
1817 //hide
1818 }
1819 }