001package ezvcard.io.json; 002 003import java.io.IOException; 004import java.io.InputStream; 005import java.io.InputStreamReader; 006import java.io.Reader; 007import java.io.StringReader; 008import java.nio.charset.StandardCharsets; 009import java.nio.file.Files; 010import java.nio.file.Path; 011 012import com.fasterxml.jackson.core.JsonParser; 013 014import ezvcard.VCard; 015import ezvcard.VCardDataType; 016import ezvcard.VCardVersion; 017import ezvcard.io.CannotParseException; 018import ezvcard.io.EmbeddedVCardException; 019import ezvcard.io.ParseWarning; 020import ezvcard.io.SkipMeException; 021import ezvcard.io.StreamReader; 022import ezvcard.io.json.JCardRawReader.JCardDataStreamListener; 023import ezvcard.io.scribe.RawPropertyScribe; 024import ezvcard.io.scribe.VCardPropertyScribe; 025import ezvcard.parameter.VCardParameters; 026import ezvcard.property.VCardProperty; 027 028/* 029 Copyright (c) 2012-2023, 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 * Path file = Paths.get("vcards.json"); 067 * try (JCardReader reader = new JCardReader(file)) { 068 * VCard vcard; 069 * while ((vcard = reader.readNext()) != null) { 070 * //... 071 * } 072 * } 073 * </pre> 074 * @author Michael Angstadt 075 * @see <a href="http://tools.ietf.org/html/rfc7095">RFC 7095</a> 076 */ 077public class JCardReader extends StreamReader { 078 private final JCardRawReader reader; 079 080 /** 081 * @param json the JSON string to read from 082 */ 083 public JCardReader(String json) { 084 this(new StringReader(json)); 085 } 086 087 /** 088 * @param in the input stream to read from 089 */ 090 public JCardReader(InputStream in) { 091 this(new InputStreamReader(in, StandardCharsets.UTF_8)); 092 } 093 094 /** 095 * @param file the file to read from 096 * @throws IOException if there is a problem reading the file 097 */ 098 public JCardReader(Path file) throws IOException { 099 this(Files.newBufferedReader(file, StandardCharsets.UTF_8)); 100 } 101 102 /** 103 * @param reader the reader to read from 104 */ 105 public JCardReader(Reader reader) { 106 this.reader = new JCardRawReader(reader); 107 } 108 109 /** 110 * @param parser the parser to read from 111 */ 112 public JCardReader(JsonParser parser) { 113 this.reader = new JCardRawReader(parser, true); 114 } 115 116 @Override 117 protected VCard _readNext() throws IOException { 118 if (reader.eof()) { 119 return null; 120 } 121 122 context.setVersion(VCardVersion.V4_0); 123 124 JCardDataStreamListenerImpl listener = new JCardDataStreamListenerImpl(); 125 reader.readNext(listener); 126 VCard vcard = listener.vcard; 127 if (vcard != null && !listener.versionFound) { 128 //@formatter:off 129 warnings.add(new ParseWarning.Builder() 130 .lineNumber(reader.getLineNum()) 131 .message(29) 132 .build() 133 ); 134 //@formatter:on 135 } 136 return vcard; 137 } 138 139 public void close() throws IOException { 140 reader.close(); 141 } 142 143 private class JCardDataStreamListenerImpl implements JCardDataStreamListener { 144 private VCard vcard = null; 145 private boolean versionFound = false; 146 147 public void beginVCard() { 148 vcard = new VCard(); 149 vcard.setVersion(VCardVersion.V4_0); 150 } 151 152 public void readProperty(String group, String propertyName, VCardParameters parameters, VCardDataType dataType, JCardValue value) { 153 context.getWarnings().clear(); 154 context.setLineNumber(reader.getLineNum()); 155 context.setPropertyName(propertyName); 156 157 if ("version".equalsIgnoreCase(propertyName)) { 158 //don't unmarshal "version" because we don't treat it as a property 159 versionFound = true; 160 161 VCardVersion version = VCardVersion.valueOfByStr(value.asSingle()); 162 if (version != VCardVersion.V4_0) { 163 //@formatter:off 164 warnings.add(new ParseWarning.Builder(context) 165 .message(30) 166 .build() 167 ); 168 //@formatter:on 169 } 170 return; 171 } 172 173 VCardPropertyScribe<? extends VCardProperty> scribe = index.getPropertyScribe(propertyName); 174 if (scribe == null) { 175 scribe = new RawPropertyScribe(propertyName); 176 } 177 178 VCardProperty property; 179 try { 180 property = scribe.parseJson(value, dataType, parameters, context); 181 warnings.addAll(context.getWarnings()); 182 } catch (SkipMeException e) { 183 //@formatter:off 184 warnings.add(new ParseWarning.Builder(context) 185 .message(22, e.getMessage()) 186 .build() 187 ); 188 //@formatter:on 189 return; 190 } catch (CannotParseException e) { 191 scribe = new RawPropertyScribe(propertyName); 192 property = scribe.parseJson(value, dataType, parameters, context); 193 194 //@formatter:off 195 warnings.add(new ParseWarning.Builder(context) 196 .message(e) 197 .build() 198 ); 199 //@formatter:on 200 } catch (EmbeddedVCardException e) { 201 //@formatter:off 202 warnings.add(new ParseWarning.Builder(context) 203 .message(31) 204 .build() 205 ); 206 //@formatter:on 207 return; 208 } 209 210 property.setGroup(group); 211 vcard.addProperty(property); 212 } 213 } 214}