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