2 * Copyright (C) 2011, Jamie Thompson
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 3 of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public
15 * License along with this program; If not, see
16 * <http://www.gnu.org/licenses/>.
19 #include "CSVSymbianEventLogParser.h"
20 #include "EventTypes/PhoneCall.h"
27 #include <QStringList>
31 using namespace EventParsers;
36 inline bool operator()(const QPair<char, uint> &a, const QPair<char, uint> &b) const
38 return b.second < a.second;
42 const QString ExtractString(const QString &originalString)
44 QRegExp content("^[\"\']?(.*)?[\"\']?$");
45 content.indexIn(originalString.trimmed());
46 return content.cap(1);
49 iEventParser *CSVSymbianEventLogParser::IsValid(const Settings ¤tSettings, QFile &eventFile)
51 qDebug() << "Checking if a CSV call log file...";
53 QByteArray firstLineContent(eventFile.readLine());
55 if(firstLineContent.length() > 0)
57 // Count the non-alphanumeric characters used
58 QHash<char, uint> counts;
59 foreach(char c, firstLineContent)
62 QList<QPair<char, uint> > orderedCounts;
63 orderedCounts.reserve(counts.size());
64 foreach(char c, counts.keys())
65 if(!QChar(c).isLetterOrNumber())
66 orderedCounts.append(QPair<char, uint>(c, counts.value(c)));
68 qSort(orderedCounts.begin(), orderedCounts.end(), SortByValueDesc());
70 // Work around Q_FOREACH macro limitation when dealing with
71 // multi-typed templates (comma issue)
72 typedef QPair<char, uint> bodge;
73 foreach(bodge count, orderedCounts)
74 qDebug() << count.first << " = " << count.second;
77 // No-one would be mad enough to use quotation marks or apostrophes
78 // as their delimiter,but just in case, check the second most
79 // frequent character is present thr right number of times for
80 // the qutation marks to be present on every column heading (two
81 // per heading, less one as they're seperators)
82 if((orderedCounts.value(0).first == '"' || orderedCounts.value(0).first == '\'')
83 && ((orderedCounts.value(0).second / 2) - 1 == orderedCounts.value(1).second ))
86 delim = orderedCounts.value(1).first;
89 delim = orderedCounts.value(0).first;
91 // Check we have the essential fields we need, and grab their
93 QStringList requiredHeadings;
94 requiredHeadings << "etype" << "etime" << "remote" << "direction"
95 << "duration" << "number" << "data";
97 EventParsers::CSVSymbianEventLogParser::ColumnIndicesHash headingPositions;
98 headingPositions.reserve(requiredHeadings.count());
100 QStringList headings(QString(firstLineContent).split(delim, QString::KeepEmptyParts, Qt::CaseSensitive));
101 int numColumnsPerRecord(headings.count());
102 for(QStringList::size_type i(0); i < headings.count(); ++i)
104 QString heading(ExtractString(headings.value(i)));
105 qDebug() << headings.value(i) << " : " << heading;
107 // Check over the required headings
108 foreach(QString requiredHeading, requiredHeadings)
110 if(heading.toLower() == requiredHeading)
112 headingPositions[requiredHeading] = i;
113 requiredHeadings.removeOne(requiredHeading);
118 // If we found all of the required headings, continue
119 if(requiredHeadings.count() == 0)
120 return new EventParsers::CSVSymbianEventLogParser(currentSettings, eventFile.fileName(), delim, numColumnsPerRecord, headingPositions);
126 CSVSymbianEventLogParser::CSVSymbianEventLogParser(const Settings &settings, const QString &filename, const char delimiter, const int numColumnsPerRecord, const ColumnIndicesHash &headingIndices)
127 : m_Settings(settings), m_Delimiter(delimiter), m_NumColumnsPerRecord(numColumnsPerRecord), m_HeadingIndices(headingIndices)
131 EventTypes::EventFromFileList CSVSymbianEventLogParser::ParseFile(QFile &eventFile, const QList<uint> &recordsToReturn)
133 qDebug() << "CSV Parsing NYI!";
134 EventTypes::EventFromFileList fileEvents;
137 QSet<uint> recordsToReturnSet(QSet<uint>::fromList(recordsToReturn));
140 uint recordNumber(0);
143 // Read the first line
144 QByteArray firstLineContent(eventFile.readLine());
145 QStringList firstLineValues(QString(firstLineContent).split(m_Delimiter));
146 if(firstLineValues.count() != m_NumColumnsPerRecord)
147 throw new std::runtime_error(QString("Unexpected number of columns (%1, expected %2) on line %3 of %4")
148 .arg(firstLineValues.count())
149 .arg(m_NumColumnsPerRecord)
151 .arg(eventFile.fileName()).toStdString());
154 // Read the main body of the file
155 while(!eventFile.atEnd())
157 QStringList lineValues(QString(eventFile.readLine()).split(m_Delimiter));
159 // Make sure we have enough columns (i.e. handle newlines in values)
160 while(lineValues.count() < m_NumColumnsPerRecord)
162 lineValues.append(QString(eventFile.readLine()).split(m_Delimiter));
166 if(recordsToReturnSet.count() == 0 || recordsToReturnSet.contains(recordNumber))
169 int eType(lineValues.at(m_HeadingIndices.value("etype")).toUInt(&bOK));
170 // We're only interested in phone calls
171 if(bOK && eType == 0)
173 qDebug() << "Parsing event from line #" << lineNumber << ". Values: " << lineValues;
175 QDateTime eTime(QDateTime::fromString(lineValues.at(m_HeadingIndices.value("etime")), "dd/MM/yyyy hh:mm:ss"));
176 int duration(lineValues.at(m_HeadingIndices.value("duration")).toInt(&bOK));
178 Settings::eDirection direction(lineValues.at(m_HeadingIndices.value("direction")) == "0"
180 : Settings::OUTGOING);
182 qDebug() << QString("Unable to parse '%1' as a duration. Skipping record.")
183 .arg(lineValues.at(m_HeadingIndices.value("duration")));
186 QString number(ExtractString(lineValues.at(m_HeadingIndices.value("number"))));
187 QString data(ExtractString(lineValues.at(m_HeadingIndices.value("data"))));
189 QSharedPointer<EventTypes::iEvent> newEvent(new EventTypes::PhoneCall(
195 fileEvents.append(EventTypes::EventFromFile(newEvent, recordNumber));
202 qDebug() << QString("File pos: %1, bAvail: %2, canReadLine: %3").arg(eventFile.pos()).arg(eventFile.bytesAvailable()).arg(eventFile.canReadLine());
203 qDebug() << fileEvents.count() << " events loaded from file";