// CalendarObject.cpp - CalendarObject class functions
//
// (c) 2016-2017 Xestia Software Development.
//
// This file is part of Xestia Calendar.
//
// Xestia Calendar 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.
//
// Xestia Calendar 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 Xestia Calendar. If not, see
#include "CalendarObject.h"
#include "../../common/file.h"
using namespace std;
CalendarObjectLoadResult CalendarObject::LoadFile(std::string loadFilename){
// Check if the file exists and return
// CALENDAROBJECTLOAD_CANNOTOPEN if not.
if (!FileExists(loadFilename)){
return CALENDAROBJECTLOAD_MISSING;
}
ifstream fileStream;
string receivedStringData = "";
fileStream.open(loadFilename, ifstream::in);
if (fileStream.rdstate() & ifstream::failbit){
return CALENDAROBJECTLOAD_CANNOTOPEN;
}
if (fileStream.rdstate() & ifstream::badbit){
return CALENDAROBJECTLOAD_CANNOTOPEN;
}
// Read the data into a string.
char *bufferRead = new char[256];
while (!fileStream.eof()){
fileStream.getline(bufferRead, 256);
receivedStringData.append(bufferRead);
receivedStringData.append("\n");
}
delete[] bufferRead;
CalendarObjectLoadResult stringProcResult = CALENDAROBJECTLOAD_UNITTESTFAIL;
stringProcResult = LoadString(&receivedStringData);
return stringProcResult;
}
CalendarObjectLoadResult CalendarObject::LoadString(std::string *loadStringData){
bool newLine = false;
bool skipMode = false;
bool colonFound = false;
bool quoteMode = false;
char bufferChar = 0;
int stringDataSize = loadStringData->size();
int seekCount = 0;
string propertyName;
string propertyValue;
while (seekCount < stringDataSize){
// Check if character is blank or a tab and is the first character
// on a new line.
if (((*loadStringData)[seekCount] == ' ' ||
(*loadStringData)[seekCount] == '\t')
&& newLine == true){
// Character is on a new line and it is a space or
// tab. Ignore this character as it is not part
// of the value.
newLine = false;
} else if ((*loadStringData)[seekCount] == '\"'){
if (quoteMode == false){
quoteMode = true;
} else {
quoteMode = false;
}
bufferChar = (*loadStringData)[seekCount];
if (colonFound == false){
propertyName += bufferChar;
} else {
propertyValue += bufferChar;
}
} else if (newLine == true){
// Character is on a new line but not a space or
// tab so check if the colon has been found
// and add the property name and value to
// the lists.
if (colonFound == true){
objectName.push_back(propertyName);
objectData.push_back(propertyValue);
}
colonFound = false;
newLine = false;
propertyName.clear();
propertyValue.clear();
bufferChar = (*loadStringData)[seekCount];
propertyName += bufferChar;
} else if ((*loadStringData)[seekCount] == '\n'){
// Character is the new line character so mark
// the NewLine boolean as true.
newLine = true;
} else if ((*loadStringData)[seekCount] == ':' &&
quoteMode == false){
// Character is the colon. Set the colon
// found boolen to true.
bufferChar = (*loadStringData)[seekCount];
if (colonFound == true){
propertyValue += bufferChar;
} else {
colonFound = true;
}
} else {
// Character is not part of a new line and is not
// the new line character itself.
bufferChar = (*loadStringData)[seekCount];
if (colonFound == false){
propertyName += bufferChar;
} else {
propertyValue += bufferChar;
}
}
seekCount++;
}
// Finish off processing any data that wasn't processed
// when the end of the string was reached.
if (colonFound == true &&
propertyName.size() > 0 &&
propertyValue.size() > 0){
objectName.push_back(propertyName);
objectData.push_back(propertyValue);
}
// Check that the object contains valid data.
CalendarObjectLoadResult stringProcResult = CALENDAROBJECTLOAD_UNITTESTFAIL;
CalendarObjectValidResult baseDataResult = ValidBaseObject();
CalendarObjectValidResult eventDataResult = ValidObject();
if (baseDataResult != CALENDAROBJECTVALID_OK ||
eventDataResult != CALENDAROBJECTVALID_OK){
stringProcResult = CALENDAROBJECTLOAD_INVALIDFORMAT;
} else {
stringProcResult = CALENDAROBJECTLOAD_OK;
}
ProcessBaseData();
ProcessData();
return stringProcResult;
}
CalendarObjectValidResult CalendarObject::ValidBaseObject(){
bool validBegin = false;
bool validAlarmBegin = false;
bool validVersion = false;
bool validEnd = false;
int seekCount = 0;
vector deleteLines;
vector alarmObjectName;
vector alarmObjectData;
// Check that the first line contains BEGIN:VCALENDAR
// and it only appears once.
for (vector::iterator iter = objectName.begin();
iter != objectName.end(); iter++){
if (objectName[seekCount] == "BEGIN" &&
objectData[seekCount] == "VCALENDAR"){
if (validBegin == false){
validBegin = true;
} else {
return CALENDAROBJECTVALID_INVALIDFORMAT;
}
}
if (objectName[seekCount] == "END" &&
objectData[seekCount] == "VCALENDAR" &&
validBegin == false){
return CALENDAROBJECTVALID_INVALIDFORMAT;
} else if (objectName[seekCount] == "END" &&
objectData[seekCount] == "VALARM" &&
validAlarmBegin == false){
return CALENDAROBJECTVALID_INVALIDFORMAT;
} else if (objectName[seekCount] == "END" &&
objectData[seekCount] == "VCALENDAR" &&
validAlarmBegin == true){
return CALENDAROBJECTVALID_INVALIDFORMAT;
}
// Look for any VALARM sections.
if (validAlarmBegin == true){
alarmObjectName.push_back(objectName[seekCount]);
alarmObjectData.push_back(objectData[seekCount]);
deleteLines.push_back(seekCount);
}
if (objectName[seekCount] == "END" &&
objectData[seekCount] == "VALARM" &&
validAlarmBegin == true){
eventAlarmName.push_back(alarmObjectName);
eventAlarmData.push_back(alarmObjectData);
alarmObjectName.clear();
alarmObjectData.clear();
validAlarmBegin = false;
}
if (objectName[seekCount] == "BEGIN" &&
objectData[seekCount] == "VALARM" &&
validBegin == true){
if (validAlarmBegin == false){
validAlarmBegin = true;
} else {
return CALENDAROBJECTVALID_INVALIDFORMAT;
}
alarmObjectName.push_back(objectName[seekCount]);
alarmObjectData.push_back(objectData[seekCount]);
deleteLines.push_back(seekCount);
}
seekCount++;
}
seekCount = 0;
// Check that the last line contains END:VCALENDAR
// and it only appears once.
for (vector::iterator iter = objectName.begin();
iter != objectName.end(); iter++){
if (objectName[seekCount] == "END" &&
objectData[seekCount] == "VCALENDAR"){
if (validEnd == false){
validEnd = true;
} else {
return CALENDAROBJECTVALID_INVALIDFORMAT;
}
}
seekCount++;
}
seekCount = 0;
// Check that the VERSION value contains 2.0 and that
// it only appears once.
for (vector::iterator iter = objectName.begin();
iter != objectName.end(); iter++){
if (objectName[seekCount] == "VERSION" &&
objectData[seekCount] == "2.0"){
if (validVersion == false){
validVersion = true;
} else {
return CALENDAROBJECTVALID_INVALIDFORMAT;
}
}
seekCount++;
}
// Remove lines that aren't needed as they have
// been moved to the EventAlarm section.
for (vector::reverse_iterator deliter = deleteLines.rbegin();
deliter != deleteLines.rend(); deliter++){
objectName.erase(objectName.begin()+(*deliter));
objectData.erase(objectData.begin()+(*deliter));
}
if (validBegin == true &&
validEnd == true &&
validVersion == true &&
validAlarmBegin == false){
return CALENDAROBJECTVALID_OK;
} else {
return CALENDAROBJECTVALID_INVALIDFORMAT;
}
}
void CalendarObject::ProcessBaseData(){
// Process the base object data.
multimap dataReceived;
// Get the method (METHOD).
dataReceived = ProcessTextVectors(&objectName, &objectData, false, "METHOD");
if (dataReceived.begin() != dataReceived.end()){
try {
methodTokens = dataReceived.begin()->first.substr(7);
}
catch(const out_of_range &oor){
// Do nothing as there is no data.
}
methodData = dataReceived.begin()->second;
}
// Get the calendar scale (CALSCALE).
dataReceived = ProcessTextVectors(&objectName, &objectData, false, "CALSCALE");
if (dataReceived.begin() != dataReceived.end()){
try {
calendarScaleTokens = dataReceived.begin()->first.substr(9);
}
catch(const out_of_range &oor){
// Do nothing as there is no data.
}
calendarScaleData = dataReceived.begin()->second;
}
}