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