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 }