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