org.projectforge.common.KeyValuePairParser.java Source code

Java tutorial

Introduction

Here is the source code for org.projectforge.common.KeyValuePairParser.java

Source

/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
//         www.projectforge.org
//
// Copyright (C) 2001-2013 Kai Reinhard (k.reinhard@micromata.de)
//
// ProjectForge is dual-licensed.
//
// This community edition is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation; version 3 of the License.
//
// This community edition is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
// Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see http://www.gnu.org/licenses/.
//
/////////////////////////////////////////////////////////////////////////////

package org.projectforge.common;

import java.io.IOException;
import java.io.Reader;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;

/**
 * Simple key value parser
 * @author K.Reinhard@micromata.com
 */
public class KeyValuePairParser {
    private char pairsSeparatorChar = KeyValuePairWriter.DEFAULT_SEPARATOR_CHAR;

    public static final String ERROR_UNEXPECTED_QUOTATIONMARK = "Unexpected quotation mark \" (only allowed in quoted cells).";

    public static final String ERROR_QUOTATIONMARK_MISSED_AT_END_OF_CELL = "Quotation \" missed at the end of cell.";

    public static final String ERROR_DELIMITER_OR_NEW_LINE_EXPECTED_AFTER_QUOTATION_MARK = "Delimiter or new line expected after quotation mark.";

    public static final String ERROR_UNEXPECTED_CHARACTER_AFTER_QUOTATION_MARK = "Unexpected character after quotation mark.";

    public static final String ERROR_INVALID_KEY = "Unexpected key. A key must be an identifier followed by '='.";

    public enum Type {
        EOF, EOL, CHAR
    }

    private final Reader source;

    private Type type;

    private int lineno = 1;

    private int colno = 0;

    private int val;

    private char cval;

    private final int pushbackBuffer[] = new int[5];

    private int pushbackIndex = -1;

    private Map<String, String> keyValuePairs;

    public KeyValuePairParser(final Reader source) {
        this.source = source;
    }

    /**
     * Returns null, if EOF.
     * @return
     */
    public Map<String, String> parse() {
        if (type == Type.EOF) {
            return null;
        }
        keyValuePairs = null;
        do {
            final String key = parseKey();
            final String value = parseValue();
            if (key != null) {
                if (keyValuePairs == null) {
                    keyValuePairs = new HashMap<String, String>();
                }
                keyValuePairs.put(key, value);
            }
        } while (type != Type.EOF && type != Type.EOL);
        return keyValuePairs;
    }

    public String getString(final String key) {
        return keyValuePairs.get(key);
    }

    public Integer getInteger(final String key) {
        final String value = keyValuePairs.get(key);
        return NumberHelper.parseInteger(value);
    }

    public BigDecimal getBigDecimal(final String key) {
        final String value = keyValuePairs.get(key);
        return NumberHelper.parseBigDecimal(value);
    }

    public String parseKey() {
        skipWhitespaces();
        final StringBuffer buf = new StringBuffer();
        while (true) {
            nextToken();
            if (type != Type.CHAR) {
                if (buf.length() > 0) {
                    throw new RuntimeException(createMessage(ERROR_INVALID_KEY, buf.toString()));
                }
                return null;
            }
            if (cval == '=') {
                break;
            } else {
                buf.append(cval);
            }
        }
        ;
        return buf.toString();
    }

    public String parseValue() {
        skipWhitespaces();
        nextToken();
        if (type != Type.CHAR || cval == pairsSeparatorChar) {
            return null;
        }
        boolean quoted = false;
        if (cval == '"') {
            quoted = true; // value is quoted.
            nextToken();
        }
        final StringBuffer buf = new StringBuffer();
        while (true) {
            if (type != Type.CHAR) {
                if (quoted == true) {
                    throw new RuntimeException(createMessage(ERROR_QUOTATIONMARK_MISSED_AT_END_OF_CELL));
                }
                return buf.toString();
            }
            if (cval == '"') {
                if (quoted == false) {
                    throw new RuntimeException(createMessage(ERROR_UNEXPECTED_QUOTATIONMARK, buf.toString()));
                }
                nextToken();
                if (type != Type.CHAR || cval == pairsSeparatorChar) { // End of cell
                    break;
                } else if (quoted == true && cval == '"') { // Escaped quotation mark
                    buf.append(cval);
                } else if (Character.isWhitespace(cval) == true) {
                    skipWhitespaces();
                    nextToken();
                    if (type != Type.CHAR || cval == pairsSeparatorChar) {
                        break;
                    } else {
                        throw new RuntimeException(
                                createMessage(ERROR_DELIMITER_OR_NEW_LINE_EXPECTED_AFTER_QUOTATION_MARK));
                    }
                } else {
                    throw new RuntimeException(createMessage(ERROR_UNEXPECTED_CHARACTER_AFTER_QUOTATION_MARK));
                }
            } else if (quoted == false && cval == pairsSeparatorChar) {
                break;
            } else {
                buf.append(cval);
            }
            nextToken();
        }
        return buf.toString();
    }

    public void setCsvSeparatorChar(final char csvSeparatorChar) {
        this.pairsSeparatorChar = csvSeparatorChar;
    }

    private String createMessage(final String msg, final String s) {
        return createMessage(msg, s, lineno, colno);
    }

    private String createMessage(final String msg) {
        return createMessage(msg, null, lineno, colno);
    }

    static String createMessage(final String msg, final String s, final int line, final int col) {
        return msg + " Error in line: " + line + " (" + col + ")" + (StringUtils.isNotBlank(s) ? ": " + s : "");
    }

    /**
     * Skips white spaces excluding new line ("\n" or "\r\n").
     */
    private void skipWhitespaces() {
        while (true) {
            nextToken();
            if (type != Type.CHAR || Character.isWhitespace(cval) == false) {
                unread();
                break;
            }
        }
    }

    public int lineno() {
        return lineno;
    }

    public boolean isIdentifierPart(final char ch) {
        return Character.isUnicodeIdentifierPart(ch);
    }

    public Type nextToken() {
        cval = 0;
        type = Type.CHAR;
        val = read();
        if (val == -1) {
            // EOF
            type = Type.EOF;
            colno = 0;
            return type;
        }
        final char c = (char) val;
        if (c == '\r') {
            val = read();
            if (val == -1) {
                unread(val); // EOF
            } else if ((char) val == '\n') {
                colno = 0;
                type = Type.EOL; // MS-DOS CR: \r\n
                return type;
            }
            unread(val); // No MS-DOS CR.
        } else if (c == '\n') {
            colno = 0;
            type = Type.EOL;
            return type;
        }
        type = Type.CHAR;
        cval = c;
        colno++;
        return type;
    }

    public void unread(final int b) {
        if (b == '\n') {
            lineno--;
            colno = 0;
        } else {
            colno--;
        }
        pushbackBuffer[++pushbackIndex] = b;
    }

    public void unread() {
        unread(val);
    }

    public int read() {
        int b;
        if (pushbackIndex >= 0) {
            b = pushbackBuffer[pushbackIndex--];
        } else {
            try {
                b = source.read();
            } catch (final IOException ex) {
                throw new RuntimeException("IOException in line: " + lineno, ex);
            }
        }
        if (b == '\n')
            lineno++;
        return b;
    }

}