001 package ezvcard; 002 003 import java.io.File; 004 import java.io.FileNotFoundException; 005 import java.io.FileReader; 006 import java.io.FileWriter; 007 import java.io.IOException; 008 import java.io.InputStream; 009 import java.io.InputStreamReader; 010 import java.io.OutputStream; 011 import java.io.OutputStreamWriter; 012 import java.io.Reader; 013 import java.io.StringWriter; 014 import java.io.Writer; 015 import java.net.URL; 016 import java.util.ArrayList; 017 import java.util.Arrays; 018 import java.util.Collection; 019 import java.util.List; 020 import java.util.Properties; 021 022 import javax.xml.transform.TransformerException; 023 024 import org.w3c.dom.Document; 025 import org.xml.sax.SAXException; 026 027 import ezvcard.io.HCardPage; 028 import ezvcard.io.HCardReader; 029 import ezvcard.io.IParser; 030 import ezvcard.io.VCardReader; 031 import ezvcard.io.VCardWriter; 032 import ezvcard.io.XCardDocument; 033 import ezvcard.io.XCardReader; 034 import ezvcard.types.VCardType; 035 import ezvcard.util.IOUtils; 036 import freemarker.template.TemplateException; 037 038 /* 039 Copyright (c) 2012, Michael Angstadt 040 All rights reserved. 041 042 Redistribution and use in source and binary forms, with or without 043 modification, are permitted provided that the following conditions are met: 044 045 1. Redistributions of source code must retain the above copyright notice, this 046 list of conditions and the following disclaimer. 047 2. Redistributions in binary form must reproduce the above copyright notice, 048 this list of conditions and the following disclaimer in the documentation 049 and/or other materials provided with the distribution. 050 051 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 052 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 053 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 054 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 055 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 056 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 057 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 058 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 059 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 060 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 061 062 The views and conclusions contained in the software and documentation are those 063 of the authors and should not be interpreted as representing official policies, 064 either expressed or implied, of the FreeBSD Project. 065 */ 066 067 /** 068 * <p> 069 * Contains helper methods for parsing/writing vCards. The methods wrap the 070 * following classes: 071 * </p> 072 * 073 * 074 * <table border="1"> 075 * <tr> 076 * <th></th> 077 * <th>Reading</th> 078 * <th>Writing</th> 079 * </tr> 080 * <tr> 081 * <th>Plain text</th> 082 * <td>{@link VCardReader}</td> 083 * <td>{@link VCardWriter}</td> 084 * </tr> 085 * <tr> 086 * <th>XML</th> 087 * <td>{@link XCardReader}</td> 088 * <td>{@link XCardDocument}</td> 089 * </tr> 090 * <tr> 091 * <th>HTML</th> 092 * <td>{@link HCardReader}</td> 093 * <td>{@link HCardPage}</td> 094 * </tr> 095 * </table> 096 * @author Michael Angstadt 097 */ 098 public class Ezvcard { 099 /** 100 * The version of the library. 101 */ 102 public static final String VERSION; 103 104 /** 105 * The project webpage. 106 */ 107 public static final String URL; 108 109 static { 110 InputStream in = null; 111 try { 112 in = Ezvcard.class.getResourceAsStream("/ez-vcard-info.properties"); 113 Properties props = new Properties(); 114 props.load(in); 115 VERSION = props.getProperty("version"); 116 URL = props.getProperty("url"); 117 } catch (IOException e) { 118 throw new RuntimeException(e); 119 } finally { 120 IOUtils.closeQuietly(in); 121 } 122 } 123 124 /** 125 * <p> 126 * Parses plain text vCards. 127 * </p> 128 * <p> 129 * Use {@link VCardReader} for more control over the parsing. 130 * </p> 131 * @param str the vCard string 132 * @return chainer object for completing the parse operation 133 * @see VCardReader 134 * @see <a href="http://www.imc.org/pdi/vcard-21.rtf">vCard 2.1</a> 135 * @see <a href="http://tools.ietf.org/html/rfc2426">RFC 2426 (3.0)</a> 136 * @see <a href="http://tools.ietf.org/html/rfc6350">RFC 6350 (4.0)</a> 137 */ 138 public static ParserChainTextString parse(String str) { 139 return new ParserChainTextString(str); 140 } 141 142 /** 143 * <p> 144 * Parses plain text vCards. 145 * </p> 146 * <p> 147 * Use {@link VCardReader} for more control over the parsing. 148 * </p> 149 * @param file the vCard file 150 * @return chainer object for completing the parse operation 151 * @throws FileNotFoundException if the file does not exist or cannot be 152 * accessed 153 * @see VCardReader 154 * @see <a href="http://www.imc.org/pdi/vcard-21.rtf">vCard 2.1</a> 155 * @see <a href="http://tools.ietf.org/html/rfc2426">RFC 2426 (3.0)</a> 156 * @see <a href="http://tools.ietf.org/html/rfc6350">RFC 6350 (4.0)</a> 157 */ 158 public static ParserChainTextReader parse(File file) throws FileNotFoundException { 159 return parse(new FileReader(file)); 160 } 161 162 /** 163 * <p> 164 * Parses plain text vCards. 165 * </p> 166 * <p> 167 * Use {@link VCardReader} for more control over the parsing. 168 * </p> 169 * @param in the input stream 170 * @return chainer object for completing the parse operation 171 * @see VCardReader 172 * @see <a href="http://www.imc.org/pdi/vcard-21.rtf">vCard 2.1</a> 173 * @see <a href="http://tools.ietf.org/html/rfc2426">RFC 2426 (3.0)</a> 174 * @see <a href="http://tools.ietf.org/html/rfc6350">RFC 6350 (4.0)</a> 175 */ 176 public static ParserChainTextReader parse(InputStream in) { 177 return parse(new InputStreamReader(in)); 178 } 179 180 /** 181 * <p> 182 * Parses plain text vCards. 183 * </p> 184 * <p> 185 * Use {@link VCardReader} for more control over the parsing. 186 * </p> 187 * @param reader the reader 188 * @return chainer object for completing the parse operation 189 * @see VCardReader 190 * @see <a href="http://www.imc.org/pdi/vcard-21.rtf">vCard 2.1</a> 191 * @see <a href="http://tools.ietf.org/html/rfc2426">RFC 2426 (3.0)</a> 192 * @see <a href="http://tools.ietf.org/html/rfc6350">RFC 6350 (4.0)</a> 193 */ 194 public static ParserChainTextReader parse(Reader reader) { 195 return new ParserChainTextReader(reader); 196 } 197 198 /** 199 * <p> 200 * Parses XML-encoded vCards (xCard). 201 * </p> 202 * <p> 203 * Use {@link XCardReader} for more control over the parsing. 204 * </p> 205 * @param xml the XML document 206 * @return chainer object for completing the parse operation 207 * @see XCardReader 208 * @see <a href="http://tools.ietf.org/html/rfc6351">RFC 6351</a> 209 */ 210 public static ParserChainXmlString parseXml(String xml) { 211 return new ParserChainXmlString(xml); 212 } 213 214 /** 215 * <p> 216 * Parses XML-encoded vCards (xCard). 217 * </p> 218 * <p> 219 * Use {@link XCardReader} for more control over the parsing. 220 * </p> 221 * @param file the XML file 222 * @return chainer object for completing the parse operation 223 * @throws FileNotFoundException if the file does not exist or cannot be 224 * accessed 225 * @see XCardReader 226 * @see <a href="http://tools.ietf.org/html/rfc6351">RFC 6351</a> 227 */ 228 public static ParserChainXmlReader parseXml(File file) throws FileNotFoundException { 229 return parseXml(new FileReader(file)); 230 } 231 232 /** 233 * <p> 234 * Parses XML-encoded vCards (xCard). 235 * </p> 236 * <p> 237 * Use {@link XCardReader} for more control over the parsing. 238 * </p> 239 * @param in the input stream to the XML document 240 * @return chainer object for completing the parse operation 241 * @see XCardReader 242 * @see <a href="http://tools.ietf.org/html/rfc6351">RFC 6351</a> 243 */ 244 public static ParserChainXmlReader parseXml(InputStream in) { 245 return parseXml(new InputStreamReader(in)); 246 } 247 248 /** 249 * <p> 250 * Parses XML-encoded vCards (xCard). 251 * </p> 252 * <p> 253 * Use {@link XCardReader} for more control over the parsing. 254 * </p> 255 * @param reader the reader to the XML document 256 * @return chainer object for completing the parse operation 257 * @see XCardReader 258 * @see <a href="http://tools.ietf.org/html/rfc6351">RFC 6351</a> 259 */ 260 public static ParserChainXmlReader parseXml(Reader reader) { 261 return new ParserChainXmlReader(reader); 262 } 263 264 /** 265 * <p> 266 * Parses XML-encoded vCards (xCard). 267 * </p> 268 * <p> 269 * Use {@link XCardReader} for more control over the parsing. 270 * </p> 271 * @param document the XML document 272 * @return chainer object for completing the parse operation 273 * @see XCardReader 274 * @see <a href="http://tools.ietf.org/html/rfc6351">RFC 6351</a> 275 */ 276 public static ParserChainXmlDom parseXml(Document document) { 277 return new ParserChainXmlDom(document); 278 } 279 280 /** 281 * <p> 282 * Parses HTML-encoded vCards (hCard). 283 * </p> 284 * <p> 285 * Use {@link HCardReader} for more control over the parsing. 286 * </p> 287 * @param html the HTML page 288 * @return chainer object for completing the parse operation 289 * @see HCardReader 290 * @see <a href="http://microformats.org/wiki/hcard">hCard 1.0</a> 291 */ 292 public static ParserChainHtmlString parseHtml(String html) { 293 return new ParserChainHtmlString(html); 294 } 295 296 /** 297 * <p> 298 * Parses HTML-encoded vCards (hCard). 299 * </p> 300 * <p> 301 * Use {@link HCardReader} for more control over the parsing. 302 * </p> 303 * @param file the HTML file 304 * @return chainer object for completing the parse operation 305 * @throws FileNotFoundException if the file does not exist or cannot be 306 * accessed 307 * @see HCardReader 308 * @see <a href="http://microformats.org/wiki/hcard">hCard 1.0</a> 309 */ 310 public static ParserChainHtmlReader parseHtml(File file) throws FileNotFoundException { 311 return parseHtml(new FileReader(file)); 312 } 313 314 /** 315 * <p> 316 * Parses HTML-encoded vCards (hCard). 317 * </p> 318 * <p> 319 * Use {@link HCardReader} for more control over the parsing. 320 * </p> 321 * @param in the input stream to the HTML page 322 * @return chainer object for completing the parse operation 323 * @see HCardReader 324 * @see <a href="http://microformats.org/wiki/hcard">hCard 1.0</a> 325 */ 326 public static ParserChainHtmlReader parseHtml(InputStream in) { 327 return parseHtml(new InputStreamReader(in)); 328 } 329 330 /** 331 * <p> 332 * Parses HTML-encoded vCards (hCard). 333 * </p> 334 * <p> 335 * Use {@link HCardReader} for more control over the parsing. 336 * </p> 337 * @param reader the reader to the HTML page 338 * @return chainer object for completing the parse operation 339 * @see HCardReader 340 * @see <a href="http://microformats.org/wiki/hcard">hCard 1.0</a> 341 */ 342 public static ParserChainHtmlReader parseHtml(Reader reader) { 343 return new ParserChainHtmlReader(reader); 344 } 345 346 /** 347 * <p> 348 * Parses HTML-encoded vCards (hCard). 349 * </p> 350 * <p> 351 * Use {@link HCardReader} for more control over the parsing. 352 * </p> 353 * @param url the URL of the webpage 354 * @return chainer object for completing the parse operation 355 * @see HCardReader 356 * @see <a href="http://microformats.org/wiki/hcard">hCard 1.0</a> 357 */ 358 public static ParserChainHtmlReader parseHtml(URL url) { 359 return new ParserChainHtmlReader(url); 360 } 361 362 /** 363 * <p> 364 * Marshals a vCard to its traditional, plain-text representation. 365 * </p> 366 * 367 * <p> 368 * Use {@link VCardWriter} for more control over how the vCard is written. 369 * </p> 370 * @param vcard the vCard to marshal 371 * @return chainer object for completing the write operation 372 * @see VCardWriter 373 * @see <a href="http://www.imc.org/pdi/vcard-21.rtf">vCard 2.1</a> 374 * @see <a href="http://tools.ietf.org/html/rfc2426">RFC 2426 (3.0)</a> 375 * @see <a href="http://tools.ietf.org/html/rfc6350">RFC 6350 (4.0)</a> 376 */ 377 public static WriterChainTextSingle write(VCard vcard) { 378 return new WriterChainTextSingle(vcard); 379 } 380 381 /** 382 * <p> 383 * Marshals multiple vCards to their traditional, plain-text representation. 384 * </p> 385 * 386 * <p> 387 * Use {@link VCardWriter} for more control over how the vCards are written. 388 * </p> 389 * @param vcards the vCards to marshal 390 * @return chainer object for completing the write operation 391 * @see VCardWriter 392 * @see <a href="http://www.imc.org/pdi/vcard-21.rtf">vCard 2.1</a> 393 * @see <a href="http://tools.ietf.org/html/rfc2426">RFC 2426 (3.0)</a> 394 * @see <a href="http://tools.ietf.org/html/rfc6350">RFC 6350 (4.0)</a> 395 */ 396 public static WriterChainTextMulti write(VCard... vcards) { 397 return write(Arrays.asList(vcards)); 398 } 399 400 /** 401 * <p> 402 * Marshals multiple vCards to their traditional, plain-text representation. 403 * </p> 404 * 405 * <p> 406 * Use {@link VCardWriter} for more control over how the vCards are written. 407 * </p> 408 * @param vcards the vCards to marshal 409 * @return chainer object for completing the write operation 410 * @see VCardWriter 411 * @see <a href="http://www.imc.org/pdi/vcard-21.rtf">vCard 2.1</a> 412 * @see <a href="http://tools.ietf.org/html/rfc2426">RFC 2426 (3.0)</a> 413 * @see <a href="http://tools.ietf.org/html/rfc6350">RFC 6350 (4.0)</a> 414 */ 415 public static WriterChainTextMulti write(Collection<VCard> vcards) { 416 return new WriterChainTextMulti(vcards); 417 } 418 419 /** 420 * <p> 421 * Marshals a vCard to its XML representation (xCard). 422 * </p> 423 * 424 * <p> 425 * Use {@link XCardDocument} for more control over how the vCard is written. 426 * </p> 427 * @param vcard the vCard to marshal 428 * @return chainer object for completing the write operation 429 * @see XCardDocument 430 * @see <a href="http://tools.ietf.org/html/rfc6351">RFC 6351</a> 431 */ 432 public static WriterChainXmlSingle writeXml(VCard vcard) { 433 return new WriterChainXmlSingle(vcard); 434 } 435 436 /** 437 * <p> 438 * Marshals multiple vCards to their XML representation (xCard). 439 * </p> 440 * 441 * <p> 442 * Use {@link XCardDocument} for more control over how the vCards are 443 * written. 444 * </p> 445 * @param vcards the vCards to marshal 446 * @return chainer object for completing the write operation 447 * @see XCardDocument 448 * @see <a href="http://tools.ietf.org/html/rfc6351">RFC 6351</a> 449 */ 450 public static WriterChainXmlMulti writeXml(VCard... vcards) { 451 return writeXml(Arrays.asList(vcards)); 452 } 453 454 /** 455 * <p> 456 * Marshals multiple vCards to their XML representation (xCard). 457 * </p> 458 * 459 * <p> 460 * Use {@link XCardDocument} for more control over how the vCards are 461 * written. 462 * </p> 463 * @param vcards the vCard to marshal 464 * @return chainer object for completing the write operation 465 * @see XCardDocument 466 * @see <a href="http://tools.ietf.org/html/rfc6351">RFC 6351</a> 467 */ 468 public static WriterChainXmlMulti writeXml(Collection<VCard> vcards) { 469 return new WriterChainXmlMulti(vcards); 470 } 471 472 /** 473 * <p> 474 * Marshals one or more vCards their HTML representation (hCard). 475 * </p> 476 * 477 * <p> 478 * Use {@link HCardPage} for more control over how the vCards are written. 479 * </p> 480 * @param vcards the vCard(s) to marshal 481 * @return chainer object for completing the write operation 482 * @see HCardPage 483 * @see <a href="http://microformats.org/wiki/hcard">hCard 1.0</a> 484 */ 485 public static WriterChainHtml writeHtml(VCard... vcards) { 486 return writeHtml(Arrays.asList(vcards)); 487 } 488 489 /** 490 * <p> 491 * Marshals one or more vCards their HTML representation (hCard). 492 * </p> 493 * 494 * <p> 495 * Use {@link HCardPage} for more control over how the vCards are written. 496 * </p> 497 * @param vcards the vCard(s) to marshal 498 * @return chainer object for completing the write operation 499 * @see HCardPage 500 * @see <a href="http://microformats.org/wiki/hcard">hCard 1.0</a> 501 */ 502 public static WriterChainHtml writeHtml(Collection<VCard> vcards) { 503 return new WriterChainHtml(vcards); 504 } 505 506 static abstract class ParserChain<T, U extends IParser> { 507 final List<Class<? extends VCardType>> extendedTypes = new ArrayList<Class<? extends VCardType>>(); 508 List<List<String>> warnings; 509 510 /** 511 * Registers an extended type class. 512 * @param typeClass the extended type class 513 * @return this 514 */ 515 @SuppressWarnings("unchecked") 516 public T register(Class<? extends VCardType> typeClass) { 517 extendedTypes.add(typeClass); 518 return (T) this; 519 } 520 521 /** 522 * Provides a list object that any unmarshal warnings will be put into. 523 * @param warnings the list object that will be populated with the 524 * warnings of each unmarshalled vCard. Each element of the list is the 525 * list of warnings for one of the unmarshalled vCards. Therefore, the 526 * size of this list will be equal to the number of parsed vCards. If a 527 * vCard does not have any warnings, then its warning list will be 528 * empty. 529 * @return this 530 */ 531 @SuppressWarnings("unchecked") 532 public T warnings(List<List<String>> warnings) { 533 this.warnings = warnings; 534 return (T) this; 535 } 536 537 /** 538 * Creates the parser. 539 * @return the parser object 540 */ 541 abstract U init() throws IOException, SAXException; 542 543 U ready() throws IOException, SAXException { 544 U parser = init(); 545 for (Class<? extends VCardType> extendedType : extendedTypes) { 546 parser.registerExtendedType(extendedType); 547 } 548 return parser; 549 } 550 551 /** 552 * Reads the first vCard from the stream. 553 * @return the vCard or null if there are no vCards 554 * @throws IOException if there's an I/O problem 555 * @throws SAXException if there's a problem parsing the XML 556 */ 557 public VCard first() throws IOException, SAXException { 558 IParser parser = ready(); 559 VCard vcard = parser.readNext(); 560 if (warnings != null) { 561 warnings.add(parser.getWarnings()); 562 } 563 return vcard; 564 } 565 566 /** 567 * Reads all vCards from the stream. 568 * @return the parsed vCards 569 * @throws IOException if there's an I/O problem 570 * @throws SAXException if there's a problem parsing the XML 571 */ 572 public List<VCard> all() throws IOException, SAXException { 573 IParser parser = ready(); 574 List<VCard> vcards = new ArrayList<VCard>(); 575 VCard vcard; 576 while ((vcard = parser.readNext()) != null) { 577 if (warnings != null) { 578 warnings.add(parser.getWarnings()); 579 } 580 vcards.add(vcard); 581 } 582 return vcards; 583 } 584 585 } 586 587 /** 588 * Convenience chainer class for parsing plain text vCards. 589 */ 590 static abstract class ParserChainText<T> extends ParserChain<T, VCardReader> { 591 boolean caretDecoding = true; 592 593 /** 594 * Sets whether the reader will decode characters in parameter values 595 * that use circumflex accent encoding (enabled by default). 596 * 597 * @param enable true to use circumflex accent decoding, false not to 598 * @see VCardReader#setCaretDecodingEnabled(boolean) 599 * @see <a href="http://tools.ietf.org/html/rfc6868">RFC 6868</a> 600 */ 601 @SuppressWarnings("unchecked") 602 public T caretDecoding(boolean enable) { 603 caretDecoding = enable; 604 return (T) this; 605 } 606 607 @Override 608 VCardReader ready() throws IOException, SAXException { 609 VCardReader parser = super.ready(); 610 parser.setCaretDecodingEnabled(caretDecoding); 611 return parser; 612 } 613 614 @Override 615 public VCard first() throws IOException { 616 try { 617 return super.first(); 618 } catch (SAXException e) { 619 //not parsing XML 620 } 621 return null; 622 } 623 624 @Override 625 public List<VCard> all() throws IOException { 626 try { 627 return super.all(); 628 } catch (SAXException e) { 629 //not parsing XML 630 } 631 return null; 632 } 633 } 634 635 /** 636 * Convenience chainer class for parsing plain text vCards. 637 */ 638 public static class ParserChainTextReader extends ParserChainText<ParserChainTextReader> { 639 private Reader reader; 640 641 private ParserChainTextReader(Reader reader) { 642 this.reader = reader; 643 } 644 645 @Override 646 public ParserChainTextReader register(Class<? extends VCardType> typeClass) { 647 return super.register(typeClass); 648 } 649 650 @Override 651 public ParserChainTextReader warnings(List<List<String>> warnings) { 652 return super.warnings(warnings); 653 } 654 655 @Override 656 public ParserChainTextReader caretDecoding(boolean enable) { 657 return super.caretDecoding(enable); 658 } 659 660 @Override 661 VCardReader init() { 662 return new VCardReader(reader); 663 } 664 } 665 666 /** 667 * Convenience chainer class for parsing plain text vCards. 668 */ 669 public static class ParserChainTextString extends ParserChainText<ParserChainTextString> { 670 private String text; 671 672 private ParserChainTextString(String text) { 673 this.text = text; 674 } 675 676 @Override 677 public ParserChainTextString register(Class<? extends VCardType> typeClass) { 678 return super.register(typeClass); 679 } 680 681 @Override 682 public ParserChainTextString warnings(List<List<String>> warnings) { 683 return super.warnings(warnings); 684 } 685 686 @Override 687 public ParserChainTextString caretDecoding(boolean enable) { 688 return super.caretDecoding(enable); 689 } 690 691 @Override 692 VCardReader init() { 693 return new VCardReader(text); 694 } 695 696 @Override 697 public VCard first() { 698 try { 699 return super.first(); 700 } catch (IOException e) { 701 //reading from string 702 } 703 return null; 704 } 705 706 @Override 707 public List<VCard> all() { 708 try { 709 return super.all(); 710 } catch (IOException e) { 711 //reading from string 712 } 713 return null; 714 } 715 } 716 717 /** 718 * Convenience chainer class for parsing XML vCards. 719 */ 720 static abstract class ParserChainXml<T> extends ParserChain<T, XCardReader> { 721 //nothing 722 } 723 724 /** 725 * Convenience chainer class for parsing XML vCards. 726 */ 727 public static class ParserChainXmlReader extends ParserChainXml<ParserChainXmlReader> { 728 private Reader reader; 729 730 private ParserChainXmlReader(Reader reader) { 731 this.reader = reader; 732 } 733 734 @Override 735 public ParserChainXmlReader register(Class<? extends VCardType> typeClass) { 736 return super.register(typeClass); 737 } 738 739 @Override 740 public ParserChainXmlReader warnings(List<List<String>> warnings) { 741 return super.warnings(warnings); 742 } 743 744 @Override 745 XCardReader init() throws IOException, SAXException { 746 return new XCardReader(reader); 747 } 748 } 749 750 /** 751 * Convenience chainer class for parsing XML vCards. 752 */ 753 public static class ParserChainXmlString extends ParserChainXml<ParserChainXmlString> { 754 private String xml; 755 756 private ParserChainXmlString(String xml) { 757 this.xml = xml; 758 } 759 760 @Override 761 public ParserChainXmlString register(Class<? extends VCardType> typeClass) { 762 return super.register(typeClass); 763 } 764 765 @Override 766 public ParserChainXmlString warnings(List<List<String>> warnings) { 767 return super.warnings(warnings); 768 } 769 770 @Override 771 XCardReader init() throws SAXException { 772 return new XCardReader(xml); 773 } 774 775 @Override 776 public VCard first() throws SAXException { 777 try { 778 return super.first(); 779 } catch (IOException e) { 780 //reading from string 781 } 782 return null; 783 } 784 785 @Override 786 public List<VCard> all() throws SAXException { 787 try { 788 return super.all(); 789 } catch (IOException e) { 790 //reading from string 791 } 792 return null; 793 } 794 } 795 796 /** 797 * Convenience chainer class for parsing XML vCards. 798 */ 799 public static class ParserChainXmlDom extends ParserChainXml<ParserChainXmlDom> { 800 private Document document; 801 802 private ParserChainXmlDom(Document document) { 803 this.document = document; 804 } 805 806 @Override 807 public ParserChainXmlDom register(Class<? extends VCardType> typeClass) { 808 return super.register(typeClass); 809 } 810 811 @Override 812 public ParserChainXmlDom warnings(List<List<String>> warnings) { 813 return super.warnings(warnings); 814 } 815 816 @Override 817 XCardReader init() { 818 return new XCardReader(document); 819 } 820 821 @Override 822 public VCard first() { 823 try { 824 return super.first(); 825 } catch (IOException e) { 826 //reading from DOM 827 } catch (SAXException e) { 828 //reading from DOM 829 } 830 return null; 831 } 832 833 @Override 834 public List<VCard> all() { 835 try { 836 return super.all(); 837 } catch (IOException e) { 838 //reading from DOM 839 } catch (SAXException e) { 840 //reading from DOM 841 } 842 return null; 843 } 844 } 845 846 /** 847 * Convenience chainer class for parsing HTML vCards. 848 */ 849 static abstract class ParserChainHtml<T> extends ParserChain<T, HCardReader> { 850 String pageUrl; 851 852 /** 853 * Sets the original URL of the webpage. This is used to resolve 854 * relative links and to set the SOURCE property on the vCard. Setting 855 * this property has no effect if reading from a {@link URL}. 856 * @param pageUrl the webpage URL 857 * @return this 858 */ 859 @SuppressWarnings("unchecked") 860 public T pageUrl(String pageUrl) { 861 this.pageUrl = pageUrl; 862 return (T) this; 863 } 864 865 @Override 866 public VCard first() throws IOException { 867 try { 868 return super.first(); 869 } catch (SAXException e) { 870 //not parsing XML 871 } 872 return null; 873 } 874 875 @Override 876 public List<VCard> all() throws IOException { 877 try { 878 return super.all(); 879 } catch (SAXException e) { 880 //not parsing XML 881 } 882 return null; 883 } 884 } 885 886 /** 887 * Convenience chainer class for parsing HTML vCards. 888 */ 889 public static class ParserChainHtmlReader extends ParserChainHtml<ParserChainHtmlReader> { 890 private Reader reader; 891 private URL url; 892 893 private ParserChainHtmlReader(Reader reader) { 894 this.reader = reader; 895 } 896 897 private ParserChainHtmlReader(URL url) { 898 this.url = url; 899 } 900 901 @Override 902 public ParserChainHtmlReader register(Class<? extends VCardType> typeClass) { 903 return super.register(typeClass); 904 } 905 906 @Override 907 public ParserChainHtmlReader warnings(List<List<String>> warnings) { 908 return super.warnings(warnings); 909 } 910 911 @Override 912 public ParserChainHtmlReader pageUrl(String pageUrl) { 913 return super.pageUrl(pageUrl); 914 } 915 916 @Override 917 HCardReader init() throws IOException { 918 return (url == null) ? new HCardReader(reader, pageUrl) : new HCardReader(url); 919 } 920 } 921 922 /** 923 * Convenience chainer class for parsing HTML vCards. 924 */ 925 public static class ParserChainHtmlString extends ParserChainHtml<ParserChainHtmlString> { 926 private String html; 927 928 private ParserChainHtmlString(String html) { 929 this.html = html; 930 } 931 932 @Override 933 HCardReader init() { 934 return new HCardReader(html, pageUrl); 935 } 936 937 @Override 938 public ParserChainHtmlString register(Class<? extends VCardType> typeClass) { 939 return super.register(typeClass); 940 } 941 942 @Override 943 public ParserChainHtmlString warnings(List<List<String>> warnings) { 944 return super.warnings(warnings); 945 } 946 947 @Override 948 public ParserChainHtmlString pageUrl(String pageUrl) { 949 return super.pageUrl(pageUrl); 950 } 951 952 @Override 953 public VCard first() { 954 try { 955 return super.first(); 956 } catch (IOException e) { 957 //reading from string 958 } 959 return null; 960 } 961 962 @Override 963 public List<VCard> all() { 964 try { 965 return super.all(); 966 } catch (IOException e) { 967 //reading from string 968 } 969 return null; 970 } 971 } 972 973 static abstract class WriterChain { 974 final Collection<VCard> vcards; 975 976 WriterChain(Collection<VCard> vcards) { 977 this.vcards = vcards; 978 } 979 } 980 981 static abstract class WriterChainText<T> extends WriterChain { 982 VCardVersion version; 983 boolean prodId = true; 984 boolean caretEncoding = false; 985 986 WriterChainText(Collection<VCard> vcards) { 987 super(vcards); 988 } 989 990 /** 991 * <p> 992 * Sets the version that all the vCards will be marshalled to. The 993 * version that is attached to each individual {@link VCard} object will 994 * be ignored. 995 * </p> 996 * <p> 997 * If no version is passed into this method, the writer will look at the 998 * version attached to each individual {@link VCard} object and marshal 999 * it to that version. And if a {@link VCard} object has no version 1000 * attached to it, then it will be marshalled to version 3.0. 1001 * </p> 1002 * @param version the version to marshal the vCards to 1003 * @return this 1004 */ 1005 @SuppressWarnings("unchecked") 1006 public T version(VCardVersion version) { 1007 this.version = version; 1008 return (T) this; 1009 } 1010 1011 /** 1012 * Sets whether or not to add a PRODID type to each vCard, saying that 1013 * the vCard was generated by this library. For 2.1 vCards, the extended 1014 * type X-PRODID is used, since PRODID is not supported by that version. 1015 * @param include true to add PRODID (default), false not to 1016 * @return this 1017 */ 1018 @SuppressWarnings("unchecked") 1019 public T prodId(boolean include) { 1020 this.prodId = include; 1021 return (T) this; 1022 } 1023 1024 /** 1025 * Sets whether the writer will use circumflex accent encoding for vCard 1026 * 3.0 and 4.0 parameter values (disabled by default). 1027 * 1028 * @param enable true to use circumflex accent encoding, false not to 1029 * @see VCardWriter#setCaretEncodingEnabled(boolean) 1030 * @see <a href="http://tools.ietf.org/html/rfc6868">RFC 6868</a> 1031 */ 1032 @SuppressWarnings("unchecked") 1033 public T caretEncoding(boolean enable) { 1034 this.caretEncoding = enable; 1035 return (T) this; 1036 } 1037 1038 /** 1039 * Writes the vCards to a string. 1040 * @return the vCard string 1041 */ 1042 public String go() { 1043 StringWriter sw = new StringWriter(); 1044 try { 1045 go(sw); 1046 } catch (IOException e) { 1047 //writing to a string 1048 } 1049 return sw.toString(); 1050 } 1051 1052 /** 1053 * Writes the vCards to an output stream. 1054 * @param out the output stream to write to 1055 * @throws IOException if there's a problem writing to the output stream 1056 */ 1057 public void go(OutputStream out) throws IOException { 1058 go(new OutputStreamWriter(out)); 1059 } 1060 1061 /** 1062 * Writes the vCards to a file. 1063 * @param file the file to write to 1064 * @throws IOException if there's a problem writing to the file 1065 */ 1066 public void go(File file) throws IOException { 1067 FileWriter writer = null; 1068 try { 1069 writer = new FileWriter(file); 1070 go(writer); 1071 } finally { 1072 IOUtils.closeQuietly(writer); 1073 } 1074 } 1075 1076 /** 1077 * Writes the vCards to a writer. 1078 * @param writer the writer to write to 1079 * @throws IOException if there's a problem writing to the writer 1080 */ 1081 public void go(Writer writer) throws IOException { 1082 VCardWriter vcardWriter = new VCardWriter(writer); 1083 if (version != null) { 1084 vcardWriter.setTargetVersion(version); 1085 } 1086 vcardWriter.setAddProdId(prodId); 1087 vcardWriter.setCaretEncodingEnabled(caretEncoding); 1088 1089 for (VCard vcard : vcards) { 1090 if (version == null) { 1091 VCardVersion vcardVersion = vcard.getVersion(); 1092 vcardWriter.setTargetVersion((vcardVersion == null) ? VCardVersion.V3_0 : vcardVersion); 1093 } 1094 vcardWriter.write(vcard); 1095 addWarnings(vcardWriter.getWarnings()); 1096 } 1097 } 1098 1099 abstract void addWarnings(List<String> warnings); 1100 } 1101 1102 /** 1103 * Convenience chainer class for writing plain text vCards 1104 */ 1105 public static class WriterChainTextMulti extends WriterChainText<WriterChainTextMulti> { 1106 private List<List<String>> warnings; 1107 1108 private WriterChainTextMulti(Collection<VCard> vcards) { 1109 super(vcards); 1110 } 1111 1112 @Override 1113 public WriterChainTextMulti version(VCardVersion version) { 1114 return super.version(version); 1115 } 1116 1117 @Override 1118 public WriterChainTextMulti prodId(boolean include) { 1119 return super.prodId(include); 1120 } 1121 1122 @Override 1123 public WriterChainTextMulti caretEncoding(boolean enable) { 1124 return super.caretEncoding(enable); 1125 } 1126 1127 /** 1128 * Provides a list object that any marshal warnings will be put into. 1129 * Warnings usually occur when there is a property in the VCard that is 1130 * not supported by the version to which the vCard is being marshalled. 1131 * @param warnings the list object that will be populated with the 1132 * warnings of each marshalled vCard. Each element of the list is the 1133 * list of warnings for one of the marshalled vCards. Therefore, the 1134 * size of this list will be equal to the number of parsed vCards. If a 1135 * vCard does not have any warnings, then its warning list will be 1136 * empty. 1137 * @return this 1138 */ 1139 public WriterChainTextMulti warnings(List<List<String>> warnings) { 1140 this.warnings = warnings; 1141 return this; 1142 } 1143 1144 @Override 1145 void addWarnings(List<String> warnings) { 1146 if (this.warnings != null) { 1147 this.warnings.add(warnings); 1148 } 1149 } 1150 } 1151 1152 /** 1153 * Convenience chainer class for writing plain text vCards 1154 */ 1155 public static class WriterChainTextSingle extends WriterChainText<WriterChainTextSingle> { 1156 private List<String> warnings; 1157 1158 private WriterChainTextSingle(VCard vcard) { 1159 super(Arrays.asList(vcard)); 1160 } 1161 1162 @Override 1163 public WriterChainTextSingle version(VCardVersion version) { 1164 return super.version(version); 1165 } 1166 1167 @Override 1168 public WriterChainTextSingle prodId(boolean include) { 1169 return super.prodId(include); 1170 } 1171 1172 @Override 1173 public WriterChainTextSingle caretEncoding(boolean enable) { 1174 return super.caretEncoding(enable); 1175 } 1176 1177 /** 1178 * Provides a list object that any marshal warnings will be put into. 1179 * Warnings usually occur when there is a property in the VCard that is 1180 * not supported by the version to which the vCard is being marshalled. 1181 * @param warnings the list object that will be populated with the 1182 * warnings of the marshalled vCard. 1183 * @return this 1184 */ 1185 public WriterChainTextSingle warnings(List<String> warnings) { 1186 this.warnings = warnings; 1187 return this; 1188 } 1189 1190 @Override 1191 void addWarnings(List<String> warnings) { 1192 if (this.warnings != null) { 1193 this.warnings.addAll(warnings); 1194 } 1195 } 1196 } 1197 1198 static abstract class WriterChainXml<T> extends WriterChain { 1199 boolean prodId = true; 1200 int indent = -1; 1201 1202 WriterChainXml(Collection<VCard> vcards) { 1203 super(vcards); 1204 } 1205 1206 /** 1207 * Sets whether or not to add a PRODID type to each vCard, saying that 1208 * the vCard was generated by this library. 1209 * @param include true to add PRODID (default), false not to 1210 * @return this 1211 */ 1212 @SuppressWarnings("unchecked") 1213 public T prodId(boolean include) { 1214 this.prodId = include; 1215 return (T) this; 1216 } 1217 1218 /** 1219 * Sets the number of indent spaces to use for pretty-printing. If not 1220 * set, then the XML will not be pretty-printed. 1221 * @param indent the number of spaces in the indent string 1222 * @return this 1223 */ 1224 @SuppressWarnings("unchecked") 1225 public T indent(int indent) { 1226 this.indent = indent; 1227 return (T) this; 1228 } 1229 1230 /** 1231 * Writes the xCards to a string. 1232 * @return the XML document 1233 */ 1234 public String go() { 1235 StringWriter sw = new StringWriter(); 1236 try { 1237 go(sw); 1238 } catch (TransformerException e) { 1239 //writing to a string 1240 } 1241 return sw.toString(); 1242 } 1243 1244 /** 1245 * Writes the xCards to an output stream. 1246 * @param out the output stream to write to 1247 * @throws TransformerException if there's a problem writing to the 1248 * output stream 1249 */ 1250 public void go(OutputStream out) throws TransformerException { 1251 go(new OutputStreamWriter(out)); 1252 } 1253 1254 /** 1255 * Writes the xCards to a file. 1256 * @param file the file to write to 1257 * @throws IOException if the file can't be opened 1258 * @throws TransformerException if there's a problem writing to the file 1259 */ 1260 public void go(File file) throws IOException, TransformerException { 1261 FileWriter writer = null; 1262 try { 1263 writer = new FileWriter(file); 1264 go(writer); 1265 } finally { 1266 IOUtils.closeQuietly(writer); 1267 } 1268 } 1269 1270 /** 1271 * Writes the xCards to a writer. 1272 * @param writer the writer to write to 1273 * @throws TransformerException if there's a problem writing to the 1274 * writer 1275 */ 1276 public void go(Writer writer) throws TransformerException { 1277 XCardDocument doc = createXCardDocument(); 1278 doc.write(writer, indent); 1279 } 1280 1281 /** 1282 * Generates an XML document object model (DOM) containing the xCards. 1283 * @return the DOM 1284 */ 1285 public Document dom() { 1286 XCardDocument doc = createXCardDocument(); 1287 return doc.getDocument(); 1288 } 1289 1290 private XCardDocument createXCardDocument() { 1291 XCardDocument doc = new XCardDocument(); 1292 doc.setAddProdId(prodId); 1293 1294 for (VCard vcard : vcards) { 1295 doc.addVCard(vcard); 1296 addWarnings(doc.getWarnings()); 1297 } 1298 1299 return doc; 1300 } 1301 1302 abstract void addWarnings(List<String> warnings); 1303 } 1304 1305 /** 1306 * Convenience chainer class for writing XML vCards (xCard). 1307 */ 1308 public static class WriterChainXmlMulti extends WriterChainXml<WriterChainXmlMulti> { 1309 private List<List<String>> warnings; 1310 1311 private WriterChainXmlMulti(Collection<VCard> vcards) { 1312 super(vcards); 1313 } 1314 1315 @Override 1316 public WriterChainXmlMulti prodId(boolean include) { 1317 return super.prodId(include); 1318 } 1319 1320 @Override 1321 public WriterChainXmlMulti indent(int indent) { 1322 return super.indent(indent); 1323 } 1324 1325 /** 1326 * Provides a list object that any marshal warnings will be put into. 1327 * Warnings usually occur when there is a property in the VCard that is 1328 * not supported by the version to which the vCard is being marshalled. 1329 * @param warnings the list object that will be populated with the 1330 * warnings of each marshalled vCard. Each element of the list is the 1331 * list of warnings for one of the marshalled vCards. Therefore, the 1332 * size of this list will be equal to the number of parsed vCards. If a 1333 * vCard does not have any warnings, then its warning list will be 1334 * empty. 1335 * @return this 1336 */ 1337 public WriterChainXmlMulti warnings(List<List<String>> warnings) { 1338 this.warnings = warnings; 1339 return this; 1340 } 1341 1342 @Override 1343 void addWarnings(List<String> warnings) { 1344 if (this.warnings != null) { 1345 this.warnings.add(warnings); 1346 } 1347 } 1348 } 1349 1350 /** 1351 * Convenience chainer class for writing XML vCards (xCard). 1352 */ 1353 public static class WriterChainXmlSingle extends WriterChainXml<WriterChainXmlSingle> { 1354 private List<String> warnings; 1355 1356 private WriterChainXmlSingle(VCard vcard) { 1357 super(Arrays.asList(vcard)); 1358 } 1359 1360 @Override 1361 public WriterChainXmlSingle prodId(boolean include) { 1362 return super.prodId(include); 1363 } 1364 1365 @Override 1366 public WriterChainXmlSingle indent(int indent) { 1367 return super.indent(indent); 1368 } 1369 1370 /** 1371 * Provides a list object that any marshal warnings will be put into. 1372 * Warnings usually occur when there is a property in the VCard that is 1373 * not supported by the version to which the vCard is being marshalled. 1374 * @param warnings the list object that will be populated with the 1375 * warnings of each marshalled vCard. 1376 * @return this 1377 */ 1378 public WriterChainXmlSingle warnings(List<String> warnings) { 1379 this.warnings = warnings; 1380 return this; 1381 } 1382 1383 @Override 1384 void addWarnings(List<String> warnings) { 1385 if (this.warnings != null) { 1386 this.warnings.addAll(warnings); 1387 } 1388 } 1389 } 1390 1391 /** 1392 * Convenience chainer class for writing HTML vCards (hCard). 1393 */ 1394 public static class WriterChainHtml extends WriterChain { 1395 private WriterChainHtml(Collection<VCard> vcards) { 1396 super(vcards); 1397 } 1398 1399 /** 1400 * Writes the hCards to a string. 1401 * @return the HTML page 1402 * @throws TemplateException if there's a problem with the freemarker 1403 * template 1404 */ 1405 public String go() throws TemplateException { 1406 StringWriter sw = new StringWriter(); 1407 try { 1408 go(sw); 1409 } catch (IOException e) { 1410 //writing string 1411 } 1412 return sw.toString(); 1413 } 1414 1415 /** 1416 * Writes the hCards to an output stream. 1417 * @param out the output stream to write to 1418 * @throws IOException if there's a problem writing to the output stream 1419 * @throws TemplateException if there's a problem with the freemarker 1420 * template 1421 */ 1422 public void go(OutputStream out) throws IOException, TemplateException { 1423 go(new OutputStreamWriter(out)); 1424 } 1425 1426 /** 1427 * Writes the hCards to a file. 1428 * @param file the file to write to 1429 * @throws IOException if there's a problem writing to the file 1430 * @throws TemplateException if there's a problem with the freemarker 1431 * template 1432 */ 1433 public void go(File file) throws IOException, TemplateException { 1434 FileWriter writer = null; 1435 try { 1436 writer = new FileWriter(file); 1437 go(writer); 1438 } finally { 1439 IOUtils.closeQuietly(writer); 1440 } 1441 } 1442 1443 /** 1444 * Writes the hCards to a writer. 1445 * @param writer the writer to write to 1446 * @throws IOException if there's a problem writing to the writer 1447 * @throws TemplateException if there's a problem with the freemarker 1448 * template 1449 */ 1450 public void go(Writer writer) throws IOException, TemplateException { 1451 HCardPage page = new HCardPage(); 1452 for (VCard vcard : vcards) { 1453 page.addVCard(vcard); 1454 } 1455 page.write(writer); 1456 } 1457 } 1458 1459 private Ezvcard() { 1460 //hide 1461 } 1462 }