Java tutorial
///////////////////////////////////////////////////////////////////////////// // // 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; } }