@Test public void parameters() throws Throwable { // @formatter:off String json = "[\"vcard\"," + "[" + "[\"prop\", { \"a\": \"one\", \"b\": [\"two\"], \"c\": [\"three\", \"four\"] }, \"text\", \"value\"]" + "]" + "]"; // @formatter:on JCardRawReader reader = createReader(json); JCardDataStreamListener listener = mock(JCardDataStreamListener.class); reader.readNext(listener); VCardParameters expected = new VCardParameters(); expected.put("a", "one"); expected.put("b", "two"); expected.put("c", "three"); expected.put("c", "four"); verify(listener).beginVCard(); verify(listener) .readProperty(null, "prop", expected, VCardDataType.TEXT, JCardValue.single("value")); verifyNoMoreInteractions(listener); }
/** * Parses the next line of the vCard file. * * @return the next line or null if there are no more lines * @throws InvalidVersionException if a VERSION property with an invalid value is encountered * @throws VCardParseException if a line cannot be parsed * @throws IOException if there's a problem reading from the input stream */ public VCardRawLine readLine() throws IOException { String line = reader.readLine(); if (line == null) { return null; } String group = null; String propertyName = null; VCardParameters parameters = new VCardParameters(); String value = null; char escapeChar = 0; // is the next char escaped? boolean inQuotes = false; // are we inside of double quotes? StringBuilder buffer = new StringBuilder(); String curParamName = null; for (int i = 0; i < line.length(); i++) { char ch = line.charAt(i); if (escapeChar != 0) { // this character was escaped if (escapeChar == '\\') { if (ch == '\\') { buffer.append(ch); } else if (ch == 'n' || ch == 'N') { // newlines appear as "\n" or "\N" (see RFC 2426 p.7) buffer.append(NEWLINE); } else if (ch == '"' && version != VCardVersion.V2_1) { // double quotes don't need to be escaped in 2.1 parameter values because they have no // special meaning buffer.append(ch); } else if (ch == ';' && version == VCardVersion.V2_1) { // semi-colons can only be escaped in 2.1 parameter values (see section 2 of specs) // if a 3.0/4.0 param value has semi-colons, the value should be surrounded in double // quotes buffer.append(ch); } else { // treat the escape character as a normal character because it's not a valid escape // sequence buffer.append(escapeChar).append(ch); } } else if (escapeChar == '^') { if (ch == '^') { buffer.append(ch); } else if (ch == 'n') { buffer.append(NEWLINE); } else if (ch == '\'') { buffer.append('"'); } else { // treat the escape character as a normal character because it's not a valid escape // sequence buffer.append(escapeChar).append(ch); } } escapeChar = 0; continue; } if (ch == '\\' || (ch == '^' && version != VCardVersion.V2_1 && caretDecodingEnabled)) { // an escape character was read escapeChar = ch; continue; } if (ch == '.' && group == null && propertyName == null) { // set the group group = buffer.toString(); buffer.setLength(0); continue; } if ((ch == ';' || ch == ':') && !inQuotes) { if (propertyName == null) { // property name propertyName = buffer.toString(); } else { // parameter value String paramValue = buffer.toString(); if (version == VCardVersion.V2_1) { // 2.1 allows whitespace to surround the "=", so remove it paramValue = StringUtils.ltrim(paramValue); } parameters.put(curParamName, paramValue); curParamName = null; } buffer.setLength(0); if (ch == ':') { // the rest of the line is the property value if (i < line.length() - 1) { value = line.substring(i + 1); } else { value = ""; } break; } continue; } if (ch == ',' && !inQuotes && version != VCardVersion.V2_1) { // multi-valued parameter parameters.put(curParamName, buffer.toString()); buffer.setLength(0); continue; } if (ch == '=' && curParamName == null) { // parameter name String paramName = buffer.toString(); if (version == VCardVersion.V2_1) { // 2.1 allows whitespace to surround the "=", so remove it paramName = StringUtils.rtrim(paramName); } curParamName = paramName; buffer.setLength(0); continue; } if (ch == '"' && version != VCardVersion.V2_1) { // 2.1 doesn't use the quoting mechanism inQuotes = !inQuotes; continue; } buffer.append(ch); } if (propertyName == null || value == null) { throw new VCardParseException(line); } if ("VERSION".equalsIgnoreCase(propertyName)) { VCardVersion version = VCardVersion.valueOfByStr(value); if (version == null) { throw new InvalidVersionException(value, line); } this.version = version; } return new VCardRawLine(group, propertyName, parameters, value); }