Search in sources :

Example 1 with VObjectProperty

use of com.github.mangstadt.vinnie.VObjectProperty in project vinnie by mangstadt.

the class VObjectReaderTest method special_characters_in_group_and_name.

/**
 * When there are special characters in the group and property name.
 */
@Test
public void special_characters_in_group_and_name() throws Exception {
    // @formatter:off
    String string = "g=,\"roup.P.=,\"ROP:value";
    for (SyntaxStyle style : SyntaxStyle.values()) {
        VObjectReader reader = reader(string, style);
        VObjectDataListenerMock listener = spy(new VObjectDataListenerMock());
        reader.parse(listener);
        InOrder inorder = inOrder(listener);
        inorder.verify(listener).onProperty(eq(new VObjectProperty("g=,\"roup", "P.=,\"ROP", "value")), any(Context.class));
        // @formatter:off
        String[] lines = string.split("\r\n");
        int line = 0;
        assertContexts(listener, context(lines[line], ++line));
    // @formatter:on
    }
}
Also used : InOrder(org.mockito.InOrder) VObjectProperty(com.github.mangstadt.vinnie.VObjectProperty) SyntaxStyle(com.github.mangstadt.vinnie.SyntaxStyle) Test(org.junit.Test)

Example 2 with VObjectProperty

use of com.github.mangstadt.vinnie.VObjectProperty in project vinnie by mangstadt.

the class VObjectReaderTest method parameter_values_in_double_quotes.

/**
 * New style syntax lets you surround parameter values in double quotes.
 * Doing this lets you put special characters like semi-colons in the
 * property value.
 */
@Test
public void parameter_values_in_double_quotes() throws Exception {
    // @formatter:off
    String string = "PROP;PARAM=\"a;b:c,d\":value";
    // @formatter:on
    Map<SyntaxStyle, VObjectProperty> styleToProperty = new HashMap<SyntaxStyle, VObjectProperty>();
    styleToProperty.put(SyntaxStyle.OLD, property().name("PROP").param("PARAM", "\"a").param(null, "b").value("c,d\":value").build());
    styleToProperty.put(SyntaxStyle.NEW, property().name("PROP").param("PARAM", "a;b:c,d").value("value").build());
    for (SyntaxStyle style : styleToProperty.keySet()) {
        VObjectProperty expectedProperty = styleToProperty.get(style);
        VObjectReader reader = reader(string, style);
        VObjectDataListenerMock listener = spy(new VObjectDataListenerMock());
        reader.parse(listener);
        InOrder inorder = inOrder(listener);
        inorder.verify(listener).onProperty(eq(expectedProperty), any(Context.class));
        // @formatter:off
        String[] lines = string.split("\r\n");
        int line = 0;
        assertContexts(listener, context(lines[line], ++line));
    // @formatter:on
    }
}
Also used : VObjectProperty(com.github.mangstadt.vinnie.VObjectProperty) InOrder(org.mockito.InOrder) HashMap(java.util.HashMap) SyntaxStyle(com.github.mangstadt.vinnie.SyntaxStyle) Test(org.junit.Test)

Example 3 with VObjectProperty

use of com.github.mangstadt.vinnie.VObjectProperty in project vinnie by mangstadt.

the class VObjectReaderTest method syntax_style_rules.

/**
 * When a parameter value doesn't have a name.
 */
@Test
public void syntax_style_rules() throws Exception {
    // @formatter:off
    String string = "VERSION:1\r\n" + "BEGIN:COMP1\r\n" + // default to OLD style
    "PROP;PARAM=\"value\":\r\n" + "END:COMP1\r\n" + "BEGIN:COMP1\r\n" + // wrong parent
    "VERSION:2\r\n" + "PROP;PARAM=\"value\":\r\n" + "END:COMP1\r\n" + "BEGIN:COMP2\r\n" + // invalid version
    "VERSION:3\r\n" + "PROP;PARAM=\"value\":\r\n" + "END:COMP2\r\n" + "BEGIN:COMP2\r\n" + "VERSION:2\r\n" + "PROP;PARAM=\"value\":\r\n" + // keep syntax of parent
    "BEGIN:COMP3\r\n" + "PROP;PARAM=\"value\":\r\n" + "END:COMP3\r\n" + // change syntax
    "BEGIN:COMP2\r\n" + "VERSION:1\r\n" + "PROP;PARAM=\"value\":\r\n" + "END:COMP2\r\n" + // syntax returns
    "PROP;PARAM=\"value\":\r\n" + "END:COMP2\r\n";
    // @formatter:on
    SyntaxRules rules = new SyntaxRules(SyntaxStyle.OLD);
    rules.addRule("COMP2", "1", SyntaxStyle.OLD);
    rules.addRule("COMP2", "2", SyntaxStyle.NEW);
    VObjectReader reader = new VObjectReader(new StringReader(string), rules);
    VObjectDataListenerMock listener = spy(new VObjectDataListenerMock());
    reader.parse(listener);
    VObjectProperty oldStyleProp = property().name("PROP").param("PARAM", "\"value\"").value("").build();
    VObjectProperty newStyleProp = property().name("PROP").param("PARAM", "value").value("").build();
    InOrder inorder = inOrder(listener);
    // @formatter:off
    inorder.verify(listener).onProperty(eq(property().name("VERSION").value("1").build()), any(Context.class));
    inorder.verify(listener).onComponentBegin(eq("COMP1"), any(Context.class));
    inorder.verify(listener).onProperty(eq(oldStyleProp), any(Context.class));
    inorder.verify(listener).onComponentEnd(eq("COMP1"), any(Context.class));
    inorder.verify(listener).onComponentBegin(eq("COMP1"), any(Context.class));
    inorder.verify(listener).onProperty(eq(property().name("VERSION").value("2").build()), any(Context.class));
    inorder.verify(listener).onProperty(eq(oldStyleProp), any(Context.class));
    inorder.verify(listener).onComponentEnd(eq("COMP1"), any(Context.class));
    inorder.verify(listener).onComponentBegin(eq("COMP2"), any(Context.class));
    inorder.verify(listener).onWarning(eq(Warning.UNKNOWN_VERSION), eq(property().name("VERSION").value("3").build()), isNull(Exception.class), any(Context.class));
    inorder.verify(listener).onProperty(eq(property().name("VERSION").value("3").build()), any(Context.class));
    inorder.verify(listener).onProperty(eq(oldStyleProp), any(Context.class));
    inorder.verify(listener).onComponentEnd(eq("COMP2"), any(Context.class));
    inorder.verify(listener).onComponentBegin(eq("COMP2"), any(Context.class));
    inorder.verify(listener).onVersion(eq("2"), any(Context.class));
    inorder.verify(listener).onProperty(eq(newStyleProp), any(Context.class));
    inorder.verify(listener).onComponentBegin(eq("COMP3"), any(Context.class));
    inorder.verify(listener).onProperty(eq(newStyleProp), any(Context.class));
    inorder.verify(listener).onComponentEnd(eq("COMP3"), any(Context.class));
    inorder.verify(listener).onComponentBegin(eq("COMP2"), any(Context.class));
    inorder.verify(listener).onVersion(eq("1"), any(Context.class));
    inorder.verify(listener).onProperty(eq(oldStyleProp), any(Context.class));
    inorder.verify(listener).onComponentEnd(eq("COMP2"), any(Context.class));
    inorder.verify(listener).onProperty(eq(newStyleProp), any(Context.class));
    inorder.verify(listener).onComponentEnd(eq("COMP2"), any(Context.class));
    // @formatter:on
    // @formatter:off
    String[] lines = string.split("\r\n");
    int line = 0;
    assertContexts(listener, context(lines[line], ++line), context(lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(asList("COMP2"), lines[line], line + 1), context(asList("COMP2"), lines[line], ++line), context(asList("COMP2"), lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(asList("COMP2"), lines[line], ++line), context(asList("COMP2"), lines[line], ++line), context(asList("COMP2"), lines[line], ++line), context(asList("COMP2", "COMP3"), lines[line], ++line), context(asList("COMP2"), lines[line], ++line), context(asList("COMP2"), lines[line], ++line), context(asList("COMP2", "COMP2"), lines[line], ++line), context(asList("COMP2", "COMP2"), lines[line], ++line), context(asList("COMP2"), lines[line], ++line), context(asList("COMP2"), lines[line], ++line), context(lines[line], ++line));
// @formatter:on
}
Also used : VObjectProperty(com.github.mangstadt.vinnie.VObjectProperty) InOrder(org.mockito.InOrder) StringReader(java.io.StringReader) IllegalCharsetNameException(java.nio.charset.IllegalCharsetNameException) DecoderException(com.github.mangstadt.vinnie.codec.DecoderException) UnsupportedCharsetException(java.nio.charset.UnsupportedCharsetException) Test(org.junit.Test)

Example 4 with VObjectProperty

use of com.github.mangstadt.vinnie.VObjectProperty in project vinnie by mangstadt.

the class VObjectWriterTest method write_VObjectProperty.

@Test
public void write_VObjectProperty() throws Exception {
    for (SyntaxStyle style : SyntaxStyle.values()) {
        StringWriter sw = new StringWriter();
        VObjectWriter writer = new VObjectWriter(sw, style);
        VObjectProperty property = new VObjectProperty();
        property.setGroup("group");
        property.setName("PROP");
        property.getParameters().put("PARAM", "pvalue");
        property.setValue("value");
        writer.writeProperty(property);
        String actual = sw.toString();
        // @formatter:off
        String expected = "group.PROP;PARAM=pvalue:value\r\n";
        // @formatter:on
        assertEquals(expected, actual);
    }
}
Also used : VObjectProperty(com.github.mangstadt.vinnie.VObjectProperty) StringWriter(java.io.StringWriter) SyntaxStyle(com.github.mangstadt.vinnie.SyntaxStyle) Test(org.junit.Test)

Example 5 with VObjectProperty

use of com.github.mangstadt.vinnie.VObjectProperty in project vinnie by mangstadt.

the class VObjectReader method parseProperty.

/**
 * Parses the next property off the input stream.
 * @param listener the data listener (for reporting warnings)
 * @return the parsed property or null if the property could not be parsed
 * @throws IOException if there was a problem reading from the input stream
 */
private VObjectProperty parseProperty(VObjectDataListener listener) throws IOException {
    VObjectProperty property = new VObjectProperty();
    /*
		 * The syntax style to assume the data is in.
		 */
    SyntaxStyle syntax = stack.peekSyntax();
    /*
		 * The name of the parameter we're currently inside of.
		 */
    String curParamName = null;
    /*
		 * The character that was used to escape the current character (for
		 * parameter values).
		 */
    char paramValueEscapeChar = 0;
    /*
		 * Are we currently inside a parameter value that is surrounded with
		 * double-quotes?
		 */
    boolean inQuotes = false;
    /*
		 * Are we currently inside the property value?
		 */
    boolean inValue = false;
    /*
		 * Does the line use quoted-printable encoding, and does it end all of
		 * its folded lines with a "=" character?
		 */
    boolean foldedQuotedPrintableLine = false;
    /*
		 * Are we currently inside the whitespace that prepends a folded line?
		 */
    boolean inFoldedLineWhitespace = false;
    /*
		 * The current character.
		 */
    char ch = 0;
    /*
		 * The previous character.
		 */
    char prevChar;
    while (true) {
        prevChar = ch;
        int read = nextChar();
        if (read < 0) {
            // end of stream
            eos = true;
            break;
        }
        ch = (char) read;
        if (prevChar == '\r' && ch == '\n') {
            /*
				 * The newline was already processed when the "\r" character was
				 * encountered, so ignore the accompanying "\n" character.
				 */
            continue;
        }
        if (isNewline(ch)) {
            foldedQuotedPrintableLine = (inValue && prevChar == '=' && property.getParameters().isQuotedPrintable());
            if (foldedQuotedPrintableLine) {
                /*
					 * Remove the "=" character that sometimes appears at the
					 * end of quoted-printable lines that are followed by a
					 * folded line.
					 */
                buffer.chop();
                context.unfoldedLine.chop();
            }
            // keep track of the current line number
            lineNumber++;
            continue;
        }
        if (isNewline(prevChar)) {
            if (isWhitespace(ch)) {
                /*
					 * This line is a continuation of the previous line (the
					 * line is folded).
					 */
                inFoldedLineWhitespace = true;
                continue;
            }
            if (foldedQuotedPrintableLine) {
            /*
					 * The property's parameters indicate that the property
					 * value is quoted-printable. And the previous line ended
					 * with an equals sign. This means that folding whitespace
					 * may not be prepended to folded lines like it should.
					 */
            } else {
                /*
					 * We're reached the end of the property.
					 */
                leftOver = ch;
                break;
            }
        }
        if (inFoldedLineWhitespace) {
            if (isWhitespace(ch) && syntax == SyntaxStyle.OLD) {
                /*
					 * 2.1 allows multiple whitespace characters to be used for
					 * folding (section 2.1.3).
					 */
                continue;
            }
            inFoldedLineWhitespace = false;
        }
        context.unfoldedLine.append(ch);
        if (inValue) {
            buffer.append(ch);
            continue;
        }
        // decode escaped parameter value character
        if (paramValueEscapeChar != 0) {
            char escapeChar = paramValueEscapeChar;
            paramValueEscapeChar = 0;
            switch(escapeChar) {
                case '\\':
                    switch(ch) {
                        case '\\':
                            buffer.append(ch);
                            continue;
                        case ';':
                            /*
						 * Semicolons can only be escaped in old style parameter
						 * values. If a new style parameter value has
						 * semicolons, the value should be surrounded in double
						 * quotes.
						 */
                            buffer.append(ch);
                            continue;
                    }
                    break;
                case '^':
                    switch(ch) {
                        case '^':
                            buffer.append(ch);
                            continue;
                        case 'n':
                            buffer.append(NEWLINE);
                            continue;
                        case '\'':
                            buffer.append('"');
                            continue;
                    }
                    break;
            }
            /*
				 * Treat the escape character as a normal character because it's
				 * not a valid escape sequence.
				 */
            buffer.append(escapeChar).append(ch);
            continue;
        }
        // check for a parameter value escape character
        if (curParamName != null) {
            switch(syntax) {
                case OLD:
                    if (ch == '\\') {
                        paramValueEscapeChar = ch;
                        continue;
                    }
                    break;
                case NEW:
                    if (ch == '^' && caretDecodingEnabled) {
                        paramValueEscapeChar = ch;
                        continue;
                    }
                    break;
            }
        }
        // set the group
        if (ch == '.' && property.getGroup() == null && property.getName() == null) {
            property.setGroup(buffer.getAndClear());
            continue;
        }
        if ((ch == ';' || ch == ':') && !inQuotes) {
            if (property.getName() == null) {
                // set the property name
                property.setName(buffer.getAndClear());
            } else {
                // set a parameter value
                String paramValue = buffer.getAndClear();
                if (syntax == SyntaxStyle.OLD) {
                    // old style allows whitespace to surround the "=", so remove it
                    paramValue = ltrim(paramValue);
                }
                property.getParameters().put(curParamName, paramValue);
                curParamName = null;
            }
            if (ch == ':') {
                // the rest of the line is the property value
                inValue = true;
            }
            continue;
        }
        if (property.getName() != null) {
            // it's a multi-valued parameter
            if (ch == ',' && curParamName != null && !inQuotes && syntax != SyntaxStyle.OLD) {
                String paramValue = buffer.getAndClear();
                property.getParameters().put(curParamName, paramValue);
                continue;
            }
            // set the parameter name
            if (ch == '=' && curParamName == null) {
                String paramName = buffer.getAndClear().toUpperCase();
                if (syntax == SyntaxStyle.OLD) {
                    // old style allows whitespace to surround the "=", so remove it
                    paramName = rtrim(paramName);
                }
                curParamName = paramName;
                continue;
            }
            // entering/leaving a double-quoted parameter value (new style only)
            if (ch == '"' && curParamName != null && syntax != SyntaxStyle.OLD) {
                inQuotes = !inQuotes;
                continue;
            }
        }
        buffer.append(ch);
    }
    /*
		 * Line or stream ended before the property value was reached.
		 */
    if (!inValue) {
        return null;
    }
    property.setValue(buffer.getAndClear());
    if (property.getParameters().isQuotedPrintable()) {
        decodeQuotedPrintable(property, listener);
    }
    return property;
}
Also used : VObjectProperty(com.github.mangstadt.vinnie.VObjectProperty) SyntaxStyle(com.github.mangstadt.vinnie.SyntaxStyle)

Aggregations

VObjectProperty (com.github.mangstadt.vinnie.VObjectProperty)10 Test (org.junit.Test)8 SyntaxStyle (com.github.mangstadt.vinnie.SyntaxStyle)7 InOrder (org.mockito.InOrder)7 DecoderException (com.github.mangstadt.vinnie.codec.DecoderException)4 IllegalCharsetNameException (java.nio.charset.IllegalCharsetNameException)4 UnsupportedCharsetException (java.nio.charset.UnsupportedCharsetException)4 StringReader (java.io.StringReader)3 StringWriter (java.io.StringWriter)1 HashMap (java.util.HashMap)1