001package ezvcard.io.json;
002
003import java.io.BufferedReader;
004import java.io.File;
005import java.io.FileNotFoundException;
006import java.io.IOException;
007import java.io.InputStream;
008import java.io.Reader;
009import java.io.StringReader;
010
011import com.fasterxml.jackson.core.JsonParser;
012
013import ezvcard.VCard;
014import ezvcard.VCardDataType;
015import ezvcard.VCardVersion;
016import ezvcard.io.CannotParseException;
017import ezvcard.io.EmbeddedVCardException;
018import ezvcard.io.ParseWarning;
019import ezvcard.io.SkipMeException;
020import ezvcard.io.StreamReader;
021import ezvcard.io.json.JCardRawReader.JCardDataStreamListener;
022import ezvcard.io.scribe.RawPropertyScribe;
023import ezvcard.io.scribe.VCardPropertyScribe;
024import ezvcard.parameter.VCardParameters;
025import ezvcard.property.VCardProperty;
026import ezvcard.util.Utf8Reader;
027
028/*
029 Copyright (c) 2012-2018, Michael Angstadt
030 All rights reserved.
031
032 Redistribution and use in source and binary forms, with or without
033 modification, are permitted provided that the following conditions are met: 
034
035 1. Redistributions of source code must retain the above copyright notice, this
036 list of conditions and the following disclaimer. 
037 2. Redistributions in binary form must reproduce the above copyright notice,
038 this list of conditions and the following disclaimer in the documentation
039 and/or other materials provided with the distribution. 
040
041 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
042 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
043 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
044 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
045 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
046 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
047 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
048 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
049 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
050 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
051
052 The views and conclusions contained in the software and documentation are those
053 of the authors and should not be interpreted as representing official policies, 
054 either expressed or implied, of the FreeBSD Project.
055 */
056
057/**
058 * <p>
059 * Parses {@link VCard} objects from a JSON data stream (jCard format).
060 * </p>
061 * <p>
062 * <b>Example:</b>
063 * </p>
064 * 
065 * <pre class="brush:java">
066 * File file = new File("vcards.json");
067 * JCardReader reader = null;
068 * try {
069 *   reader = new JCardReader(file);
070 *   VCard vcard;
071 *   while ((vcard = reader.readNext()) != null) {
072 *     //...
073 *   }
074 * } finally {
075 *   if (reader != null) reader.close();
076 * }
077 * </pre>
078 * @author Michael Angstadt
079 * @see <a href="http://tools.ietf.org/html/rfc7095">RFC 7095</a>
080 */
081public class JCardReader extends StreamReader {
082        private final JCardRawReader reader;
083
084        /**
085         * @param json the JSON string to read from
086         */
087        public JCardReader(String json) {
088                this(new StringReader(json));
089        }
090
091        /**
092         * @param in the input stream to read from
093         */
094        public JCardReader(InputStream in) {
095                this(new Utf8Reader(in));
096        }
097
098        /**
099         * @param file the file to read from
100         * @throws FileNotFoundException if the file doesn't exist
101         */
102        public JCardReader(File file) throws FileNotFoundException {
103                this(new BufferedReader(new Utf8Reader(file)));
104        }
105
106        /**
107         * @param reader the reader to read from
108         */
109        public JCardReader(Reader reader) {
110                this.reader = new JCardRawReader(reader);
111        }
112
113        /**
114         * @param parser the parser to read from
115         */
116        public JCardReader(JsonParser parser) {
117                this.reader = new JCardRawReader(parser, true);
118        }
119
120        @Override
121        protected VCard _readNext() throws IOException {
122                if (reader.eof()) {
123                        return null;
124                }
125
126                context.setVersion(VCardVersion.V4_0);
127
128                JCardDataStreamListenerImpl listener = new JCardDataStreamListenerImpl();
129                reader.readNext(listener);
130                VCard vcard = listener.vcard;
131                if (vcard != null && !listener.versionFound) {
132                        //@formatter:off
133                        warnings.add(new ParseWarning.Builder()
134                                .lineNumber(reader.getLineNum())
135                                .message(29)
136                                .build()
137                        );
138                        //@formatter:on
139                }
140                return vcard;
141        }
142
143        public void close() throws IOException {
144                reader.close();
145        }
146
147        private class JCardDataStreamListenerImpl implements JCardDataStreamListener {
148                private VCard vcard = null;
149                private boolean versionFound = false;
150
151                public void beginVCard() {
152                        vcard = new VCard();
153                        vcard.setVersion(VCardVersion.V4_0);
154                }
155
156                public void readProperty(String group, String propertyName, VCardParameters parameters, VCardDataType dataType, JCardValue value) {
157                        context.getWarnings().clear();
158                        context.setLineNumber(reader.getLineNum());
159                        context.setPropertyName(propertyName);
160
161                        if ("version".equalsIgnoreCase(propertyName)) {
162                                //don't unmarshal "version" because we don't treat it as a property
163                                versionFound = true;
164
165                                VCardVersion version = VCardVersion.valueOfByStr(value.asSingle());
166                                if (version != VCardVersion.V4_0) {
167                                        //@formatter:off
168                                        warnings.add(new ParseWarning.Builder(context)
169                                                .message(30)
170                                                .build()
171                                        );
172                                        //@formatter:on
173                                }
174                                return;
175                        }
176
177                        VCardPropertyScribe<? extends VCardProperty> scribe = index.getPropertyScribe(propertyName);
178                        if (scribe == null) {
179                                scribe = new RawPropertyScribe(propertyName);
180                        }
181
182                        VCardProperty property;
183                        try {
184                                property = scribe.parseJson(value, dataType, parameters, context);
185                                warnings.addAll(context.getWarnings());
186                        } catch (SkipMeException e) {
187                                //@formatter:off
188                                warnings.add(new ParseWarning.Builder(context)
189                                        .message(22, e.getMessage())
190                                        .build()
191                                );
192                                //@formatter:on
193                                return;
194                        } catch (CannotParseException e) {
195                                scribe = new RawPropertyScribe(propertyName);
196                                property = scribe.parseJson(value, dataType, parameters, context);
197
198                                //@formatter:off
199                                warnings.add(new ParseWarning.Builder(context)
200                                        .message(e)
201                                        .build()
202                                );
203                                //@formatter:on
204                        } catch (EmbeddedVCardException e) {
205                                //@formatter:off
206                                warnings.add(new ParseWarning.Builder(context)
207                                        .message(31)
208                                        .build()
209                                );
210                                //@formatter:on
211                                return;
212                        }
213
214                        property.setGroup(group);
215                        vcard.addProperty(property);
216                }
217        }
218}