class DocViewProperty {

    public static DocViewProperty parse(String name, String value) {
        boolean isMulti = false;
        boolean isBinaryRef = false;
        int type = PropertyType.UNDEFINED;
        int pos = 0;
        char state = 'b';
        List<String> vals = null;
        StringBuffer tmp = new StringBuffer();
        int unicode = 0;
        int unicodePos = 0;
        while (pos < value.length()) {
            char c = value.charAt(pos++);
            switch (state) {
                case 'b': // begin (type or array or value)
                    if (c == '{') {
                        state = 't';
                    } else if (c == '[') {
                        isMulti = true;
                        state = 'v';
                    } else if (c == '\\') {
                        state = 'e';
                    } else {
                        tmp.append(c);
                        state = 'v';
                    }
                    break;
                case 'a': // array (array or value)
                    if (c == '[') {
                        isMulti = true;
                        state = 'v';
                    } else if (c == '\\') {
                        state = 'e';
                    } else {
                        tmp.append(c);
                        state = 'v';
                    }
                    break;
                case 't':
                    if (c == '}') {
                        if (BINARY_REF.equals(tmp.toString())) {
                            type = PropertyType.BINARY;
                            isBinaryRef = true;
                        } else {
                            type = PropertyType.valueFromName(tmp.toString());
                        }
                        tmp.setLength(0);
                        state = 'a';
                    } else {
                        tmp.append(c);
                    }
                    break;
                case 'v': // value
                    if (c == '\\') {
                        state = 'e';
                    } else if (c == ',' && isMulti) {
                        if (vals == null) {
                            vals = new LinkedList<String>();
                        }
                        vals.add(tmp.toString());
                        tmp.setLength(0);
                    } else if (c == ']' && isMulti && pos == value.length()) {
                        if (tmp.length() > 0 || vals != null) {
                            if (vals == null) {
                                vals = new LinkedList<String>();
                            }
                            vals.add(tmp.toString());
                            tmp.setLength(0);
                        }
                    } else {
                        tmp.append(c);
                    }
                    break;
                case 'e': // escaped
                    if (c == 'u') {
                        state = 'u';
                        unicode = 0;
                        unicodePos = 0;
                    } else if (c == '0') {
                        // special case to treat empty values. see JCR-3661
                        state = 'v';
                        if (vals == null) {
                            vals = new LinkedList<String>();
                        }
                    } else {
                        state = 'v';
                        tmp.append(c);
                    }
                    break;
                case 'u': // unicode escaped
                    unicode = (unicode << 4) + Character.digit(c, 16);
                    if (++unicodePos == 4) {
                        tmp.appendCodePoint(unicode);
                        state = 'v';
                    }
                    break;

            }
        }
        String[] values;
        if (isMulti) {
            // add value if missing ']'
            if (tmp.length() > 0) {
                if (vals == null) {
                    vals = new LinkedList<String>();
                }
                vals.add(tmp.toString());
            }
            if (vals == null) {
                values = Constants.EMPTY_STRING_ARRAY;
            } else {
                values = vals.toArray(new String[vals.size()]);
            }
        } else {
            values = new String[]{tmp.toString()};
        }
        return new DocViewProperty(name, values, isMulti, type, isBinaryRef);
    }

    public  DocViewProperty(String name, String[] values, boolean multi, int type);
    public  DocViewProperty(String name, String[] values, boolean multi, int type, boolean isRef);

}