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 }