class ContentDisposition {

    static ContentDisposition parse(String input) {
        Objects.requireNonNull(input, "Parameter 'input' is null!");
        Tokenizer tokenizer = new Tokenizer(input.trim());
        try {
            String type = tokenizer.consumeToken(TOKEN_MATCHER).toLowerCase();
            Map<String, String> parameters = new HashMap<>();
            while (tokenizer.hasMore()) {
                tokenizer.consumeTokenIfPresent(LINEAR_WHITE_SPACE);
                tokenizer.consumeCharacter(';');
                tokenizer.consumeTokenIfPresent(LINEAR_WHITE_SPACE);
                String attribute = tokenizer.consumeToken(TOKEN_MATCHER);
                tokenizer.consumeCharacter('=');
                final String value;
                if ('"' == tokenizer.previewChar()) {
                    tokenizer.consumeCharacter('"');
                    StringBuilder valueBuilder = new StringBuilder();
                    while ('"' != tokenizer.previewChar()) {
                        // quoted pair
                        // '\' escapes '"' or '\'
                        if ('\\' == tokenizer.previewChar()) {
                            tokenizer.consumeCharacter('\\');
                            char c = tokenizer.previewChar();
                            if ('"' == c || '\\' == c) {
                                // process
                                valueBuilder.append(tokenizer.consumeCharacter(CharMatcher.ascii()));
                                continue;
                            } else {
                                valueBuilder.append('\\');
                            }
                        }
                        valueBuilder.append(tokenizer.consumeToken(QUOTED_TEXT_MATCHER));
                    }
                    value = valueBuilder.toString();
                    tokenizer.consumeCharacter('"');
                } else {
                    value = tokenizer.consumeToken(TOKEN_MATCHER);
                }
                parameters.put(attribute, value);
            }
            return new ContentDisposition(type, parameters);
        } catch (IllegalStateException e) {
            throw new IllegalArgumentException("Could not parse '" + input + "'", e);
        }
    }

    private  ContentDisposition(String type, Map<String, String> params);

}