1 // CalendarObject.cpp - CalendarObject class functions
3 // (c) 2016-2017 Xestia Software Development.
5 // This file is part of Xestia Calendar.
7 // Xestia Calendar is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by the
9 // Free Software Foundation, version 3 of the license.
11 // Xestia Calendar is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License along
17 // with Xestia Calendar. If not, see <http://www.gnu.org/licenses/>
19 #include "CalendarObject.h"
20 #include "../../common/file.h"
24 CalendarObjectLoadResult CalendarObject::LoadFile(std::string loadFilename){
26 // Check if the file exists and return
27 // CALENDAROBJECTLOAD_CANNOTOPEN if not.
30 string receivedStringData = "";
34 if (!FileExists(loadFilename)) {
35 return CALENDAROBJECTLOAD_MISSING;
38 fileStream.open(loadFilename, ifstream::in);
42 wstring loadFilename_utf16;
44 int len = MultiByteToWideChar(CP_UTF8, 0, &loadFilename[0], (int)loadFilename.size(), NULL, 0);
48 loadFilename_utf16.resize(len);
49 MultiByteToWideChar(CP_UTF8, 0, &loadFilename[0], (int)loadFilename.size(), &loadFilename_utf16[0], len);
52 if (!FileExists(loadFilename_utf16)) {
53 return CALENDAROBJECTLOAD_MISSING;
56 fileStream.open(loadFilename_utf16, ifstream::in);
60 if (fileStream.rdstate() & ifstream::failbit){
61 return CALENDAROBJECTLOAD_CANNOTOPEN;
64 if (fileStream.rdstate() & ifstream::badbit){
65 return CALENDAROBJECTLOAD_CANNOTOPEN;
68 // Read the data into a string.
70 char *bufferRead = new char[256];
72 while (!fileStream.eof()){
74 fileStream.getline(bufferRead, 256);
75 receivedStringData.append(bufferRead);
76 receivedStringData.append("\n");
82 CalendarObjectLoadResult stringProcResult = CALENDAROBJECTLOAD_UNITTESTFAIL;
84 stringProcResult = LoadString(&receivedStringData);
86 return stringProcResult;
90 CalendarObjectLoadResult CalendarObject::LoadString(std::string *loadStringData){
93 bool skipMode = false;
94 bool colonFound = false;
95 bool quoteMode = false;
97 int stringDataSize = loadStringData->size();
100 string propertyValue;
102 while (seekCount < stringDataSize){
104 // Check if character is blank or a tab and is the first character
107 if (((*loadStringData)[seekCount] == ' ' ||
108 (*loadStringData)[seekCount] == '\t')
111 // Character is on a new line and it is a space or
112 // tab. Ignore this character as it is not part
117 } else if ((*loadStringData)[seekCount] == '\"'){
119 if (quoteMode == false){
125 bufferChar = (*loadStringData)[seekCount];
127 if (colonFound == false){
128 propertyName += bufferChar;
130 propertyValue += bufferChar;
133 } else if (newLine == true){
135 // Character is on a new line but not a space or
136 // tab so check if the colon has been found
137 // and add the property name and value to
140 if (colonFound == true){
141 objectName.push_back(propertyName);
142 objectData.push_back(propertyValue);
147 propertyName.clear();
148 propertyValue.clear();
150 bufferChar = (*loadStringData)[seekCount];
151 propertyName += bufferChar;
153 } else if ((*loadStringData)[seekCount] == '\n'){
155 // Character is the new line character so mark
156 // the NewLine boolean as true.
160 } else if ((*loadStringData)[seekCount] == ':' &&
163 // Character is the colon. Set the colon
164 // found boolen to true.
166 bufferChar = (*loadStringData)[seekCount];
168 if (colonFound == true){
169 propertyValue += bufferChar;
176 // Character is not part of a new line and is not
177 // the new line character itself.
179 bufferChar = (*loadStringData)[seekCount];
181 if (colonFound == false){
182 propertyName += bufferChar;
184 propertyValue += bufferChar;
193 // Finish off processing any data that wasn't processed
194 // when the end of the string was reached.
196 if (colonFound == true &&
197 propertyName.size() > 0 &&
198 propertyValue.size() > 0){
200 objectName.push_back(propertyName);
201 objectData.push_back(propertyValue);
205 // Check that the object contains valid data.
207 CalendarObjectLoadResult stringProcResult = CALENDAROBJECTLOAD_UNITTESTFAIL;
208 CalendarObjectValidResult baseDataResult = ValidBaseObject();
209 CalendarObjectValidResult eventDataResult = ValidObject();
211 if (baseDataResult != CALENDAROBJECTVALID_OK ||
212 eventDataResult != CALENDAROBJECTVALID_OK){
214 stringProcResult = CALENDAROBJECTLOAD_INVALIDFORMAT;
218 stringProcResult = CALENDAROBJECTLOAD_OK;
225 return stringProcResult;
229 CalendarObjectValidResult CalendarObject::ValidBaseObject(){
231 bool validBegin = false;
232 bool validAlarmBegin = false;
233 bool validVersion = false;
234 bool validEnd = false;
236 vector<int> deleteLines;
237 vector<string> alarmObjectName;
238 vector<string> alarmObjectData;
240 // Check that the first line contains BEGIN:VCALENDAR
241 // and it only appears once.
243 for (vector<string>::iterator iter = objectName.begin();
244 iter != objectName.end(); iter++){
246 if (objectName[seekCount] == "BEGIN" &&
247 objectData[seekCount] == "VCALENDAR"){
249 if (validBegin == false){
252 return CALENDAROBJECTVALID_INVALIDFORMAT;
257 if (objectName[seekCount] == "END" &&
258 objectData[seekCount] == "VCALENDAR" &&
259 validBegin == false){
261 return CALENDAROBJECTVALID_INVALIDFORMAT;
263 } else if (objectName[seekCount] == "END" &&
264 objectData[seekCount] == "VALARM" &&
265 validAlarmBegin == false){
267 return CALENDAROBJECTVALID_INVALIDFORMAT;
269 } else if (objectName[seekCount] == "END" &&
270 objectData[seekCount] == "VCALENDAR" &&
271 validAlarmBegin == true){
273 return CALENDAROBJECTVALID_INVALIDFORMAT;
277 // Look for any VALARM sections.
279 if (validAlarmBegin == true){
281 alarmObjectName.push_back(objectName[seekCount]);
282 alarmObjectData.push_back(objectData[seekCount]);
283 deleteLines.push_back(seekCount);
287 if (objectName[seekCount] == "END" &&
288 objectData[seekCount] == "VALARM" &&
289 validAlarmBegin == true){
291 eventAlarmName.push_back(alarmObjectName);
292 eventAlarmData.push_back(alarmObjectData);
294 alarmObjectName.clear();
295 alarmObjectData.clear();
297 validAlarmBegin = false;
301 if (objectName[seekCount] == "BEGIN" &&
302 objectData[seekCount] == "VALARM" &&
305 if (validAlarmBegin == false){
306 validAlarmBegin = true;
308 return CALENDAROBJECTVALID_INVALIDFORMAT;
311 alarmObjectName.push_back(objectName[seekCount]);
312 alarmObjectData.push_back(objectData[seekCount]);
313 deleteLines.push_back(seekCount);
323 // Check that the last line contains END:VCALENDAR
324 // and it only appears once.
326 for (vector<string>::iterator iter = objectName.begin();
327 iter != objectName.end(); iter++){
329 if (objectName[seekCount] == "END" &&
330 objectData[seekCount] == "VCALENDAR"){
332 if (validEnd == false){
335 return CALENDAROBJECTVALID_INVALIDFORMAT;
346 // Check that the VERSION value contains 2.0 and that
347 // it only appears once.
349 for (vector<string>::iterator iter = objectName.begin();
350 iter != objectName.end(); iter++){
352 if (objectName[seekCount] == "VERSION" &&
353 objectData[seekCount] == "2.0"){
355 if (validVersion == false){
358 return CALENDAROBJECTVALID_INVALIDFORMAT;
367 // Remove lines that aren't needed as they have
368 // been moved to the EventAlarm section.
370 for (vector<int>::reverse_iterator deliter = deleteLines.rbegin();
371 deliter != deleteLines.rend(); deliter++){
373 objectName.erase(objectName.begin()+(*deliter));
374 objectData.erase(objectData.begin()+(*deliter));
378 if (validBegin == true &&
380 validVersion == true &&
381 validAlarmBegin == false){
383 return CALENDAROBJECTVALID_OK;
387 return CALENDAROBJECTVALID_INVALIDFORMAT;
393 void CalendarObject::ProcessBaseData(){
395 // Process the base object data.
397 multimap<string,string> dataReceived;
399 // Get the method (METHOD).
401 dataReceived = ProcessTextVectors(&objectName, &objectData, false, "METHOD");
403 if (dataReceived.begin() != dataReceived.end()){
406 methodTokens = dataReceived.begin()->first.substr(7);
409 catch(const out_of_range &oor){
410 // Do nothing as there is no data.
413 methodData = dataReceived.begin()->second;
417 // Get the calendar scale (CALSCALE).
419 dataReceived = ProcessTextVectors(&objectName, &objectData, false, "CALSCALE");
421 if (dataReceived.begin() != dataReceived.end()){
424 calendarScaleTokens = dataReceived.begin()->first.substr(9);
427 catch(const out_of_range &oor){
428 // Do nothing as there is no data.
431 calendarScaleData = dataReceived.begin()->second;