/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.redgeek.android.eventrend.importing; import java.util.ArrayList; import net.redgeek.android.eventrend.db.CategoryDbTable; import net.redgeek.android.eventrend.db.EntryDbTable; /** * Probably the worst CSV parser ever created. Supports minimal CSV I/O, and * understands local DB row/column formats for importing into local DB records. * Note resilient to mal-formed CSV. * * <strong>This should really be made into a proper parser, not this ad-hoc * beast.</strong> * * @author barclay */ public class CSV { /** * Utility routine. Joins the array of strings with commas (','). Does no * quoting of strings. Result is only newline terminated if the last entry in * <code>s</code> is newline terminated. * * @param s * The array of strings to join. * @return The result of the concatenations. */ public static String joinCSV(String[] s) { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < s.length; i++) { buffer.append(s[i]); if (i < s.length - 1) buffer.append(","); } return buffer.toString(); } /** * Utility routine. Calls {@link #joinCSV(String[])} and append a newline * ('\n') character. * * @param s * @return The result of the concatenation of {@link #joinCSV(String[])} and a * newline. * @see #joinCSV(String[]) */ public static String joinCSVTerminated(String[] s) { return joinCSV(s) + "\n"; } /** * Takes a CategoryDbTable.Row and joins it's fields into a comma-separated * string. Only fields that are listed in the Row class's EXPORTABLE array * will be exported, and special provisions are made to convert boolean fields * to either 1 or 0. All other fields are ignored. * * @param row * The Row to join the fields of. * @return The comma-separated string. * @see net.redgeek.android.eventrend.db.CategoryDbTable.Row */ public static String joinCSV(CategoryDbTable.Row row) { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < CategoryDbTable.EXPORTABLE.length; i++) { if (CategoryDbTable.EXPORTABLE[i].equals(CategoryDbTable.KEY_ROWID)) buffer.append(row.getId()); else if (CategoryDbTable.EXPORTABLE[i] .equals(CategoryDbTable.KEY_GROUP_NAME)) buffer.append(row.getGroupName()); else if (CategoryDbTable.EXPORTABLE[i] .equals(CategoryDbTable.KEY_CATEGORY_NAME)) buffer.append(row.getCategoryName()); else if (CategoryDbTable.EXPORTABLE[i] .equals(CategoryDbTable.KEY_DEFAULT_VALUE)) buffer.append(row.getDefaultValue()); else if (CategoryDbTable.EXPORTABLE[i] .equals(CategoryDbTable.KEY_LAST_TREND)) buffer.append(row.getLastTrend()); else if (CategoryDbTable.EXPORTABLE[i] .equals(CategoryDbTable.KEY_INCREMENT)) buffer.append(row.getIncrement()); else if (CategoryDbTable.EXPORTABLE[i].equals(CategoryDbTable.KEY_GOAL)) buffer.append(row.getGoal()); else if (CategoryDbTable.EXPORTABLE[i].equals(CategoryDbTable.KEY_COLOR)) buffer.append(row.getColor()); else if (CategoryDbTable.EXPORTABLE[i].equals(CategoryDbTable.KEY_TYPE)) buffer.append(row.getType()); else if (CategoryDbTable.EXPORTABLE[i] .equals(CategoryDbTable.KEY_PERIOD_MS)) buffer.append(row.getPeriodMs()); else if (CategoryDbTable.EXPORTABLE[i].equals(CategoryDbTable.KEY_RANK)) buffer.append(row.getRank()); else if (CategoryDbTable.EXPORTABLE[i] .equals(CategoryDbTable.KEY_TREND_STATE)) buffer.append(row.getTrendState()); else if (CategoryDbTable.EXPORTABLE[i] .equals(CategoryDbTable.KEY_INTERPOLATION)) buffer.append(row.getInterpolation()); else if (CategoryDbTable.EXPORTABLE[i] .equals(CategoryDbTable.KEY_ZEROFILL)) { if (row.getZeroFill() == false) buffer.append("0"); else buffer.append("1"); } else if (CategoryDbTable.EXPORTABLE[i] .equals(CategoryDbTable.KEY_SYNTHETIC)) { if (row.getSynthetic() == false) buffer.append("0"); else buffer.append("1"); } else if (CategoryDbTable.EXPORTABLE[i] .equals(CategoryDbTable.KEY_FORMULA)) buffer.append(row.getFormula()); if (i < CategoryDbTable.EXPORTABLE.length - 1) buffer.append(","); } return buffer.toString(); } /** * Takes a n EntryDbTable.Row and joins it's fields into a comma-separated * string. Only fields that are listed in the Row class's EXPORTABLE array * will be exported. All other fields are ignored. * * @param row * The Row to join the fields of. * @return The comma-separated string. * @see net.redgeek.android.eventrend.db.EntryDbTable.Row */ public static String joinCSV(EntryDbTable.Row row) { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < EntryDbTable.EXPORTABLE.length; i++) { if (EntryDbTable.EXPORTABLE[i].equals(EntryDbTable.KEY_ROWID)) buffer.append(row.getId()); else if (EntryDbTable.EXPORTABLE[i].equals(EntryDbTable.KEY_CATEGORY_ID)) buffer.append(row.getCategoryId()); else if (EntryDbTable.EXPORTABLE[i].equals(EntryDbTable.KEY_TIMESTAMP)) buffer.append(row.getTimestamp()); else if (EntryDbTable.EXPORTABLE[i].equals(EntryDbTable.KEY_VALUE)) buffer.append(row.getValue()); else if (EntryDbTable.EXPORTABLE[i].equals(EntryDbTable.KEY_N_ENTRIES)) buffer.append(row.getNEntries()); if (i < EntryDbTable.EXPORTABLE.length - 1) buffer.append(","); } return buffer.toString(); } /** * Parses a String assumed to be the complete header of a CSV file, that is, a * listing of the field names, and returns the values as an ArrayList. Field * names may not contain commas, else they will be treated as separate fields. * Quoted field names (in order to support field names with commas or other * exceptional characters, like newlines) are not supported. * * @param line * A String containing the CSV file header. * @return An ArrayList of Strings, ordered in the same order as CSV field * headers occurred in <code>line</code>. */ public static ArrayList<String> parseHeader(String line) { ArrayList<String> columns = new ArrayList<String>(); int i = 0; int startIndex = 0; int nextComma = 0; for (startIndex = 0, i = 0; startIndex < line.length(); startIndex++, i++) { nextComma = line.indexOf(',', startIndex); if (nextComma == -1) nextComma = line.length(); String column = line.substring(startIndex, nextComma); columns.add(i, column); startIndex = nextComma; } return columns; } /** * Given a String representing a (non-header) line of CSV, and a character * index to begin processing at, returns the next data field of the line as a * String. Supports double-quote ('"') delimited fields. If there is no such * entry, either due to repeated commas (e.g., ",," indicating an empty field) * or end-of-line, an empty String is returned (""). This is not resilient to * malformed CSV. * * @param line * The line of text to process. * @param startIndex * The index to begin process at. This should be the first valid * character of the next field, not the (possible) comma before the * field. * */ public static String getNextLineEntry(String line, int startIndex) { boolean inQuotes = false; int i, entryStart, entryEnd; char c; entryStart = startIndex; entryEnd = startIndex; if (startIndex >= line.length()) { entryStart = line.length(); entryEnd = entryStart; } for (i = 0; startIndex < line.length(); startIndex++, i++) { c = line.charAt(startIndex); if (inQuotes == false && c == ',') { entryEnd = startIndex; break; } if (i == 0 && c == '"') { inQuotes = true; } if (i > 0 && inQuotes == true && c == '"') { inQuotes = false; } entryEnd = startIndex; } if (startIndex == line.length()) entryEnd++; return line.substring(entryStart, entryEnd); } /** * Parses a data line of a CSV file and returns the results as an ArrayList * ordered in the original order of the line. * * @param line * The line to process. * @return An ArrayList of Strings representing the field values. */ public static ArrayList<String> getNextLine(String line) { ArrayList<String> entries = new ArrayList<String>(); String item; int i = 0; int startIndex = 0; for (startIndex = 0, i = 0; startIndex < line.length(); i++) { item = CSV.getNextLineEntry(line, startIndex); startIndex += item.length() + 1; // Remove quotes around quoted strings if (item.length() > 1 && item.charAt(0) == '"' && item.charAt(item.length() - 1) == '"') { int newStart = 1; int newEnd = item.length() - 1; if (newEnd < newStart) newEnd = newStart; item = item.substring(newStart, newEnd); } entries.add(i, item); } return entries; } }