Home | News | Projects | Releases
Bugs | RFE | Repositories | Help
Win32: implement further UTF8 support
[xestiacalendar/.git] / source / objects / calendarobject / CalendarObject.cpp
1 // CalendarObject.cpp - CalendarObject class functions
2 //
3 // (c) 2016-2017 Xestia Software Development.
4 //
5 // This file is part of Xestia Calendar.
6 //
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.
10 //
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.
15 //
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"
22 using namespace std;
24 CalendarObjectLoadResult CalendarObject::LoadFile(std::string loadFilename){
26         // Check if the file exists and return 
27         // CALENDAROBJECTLOAD_CANNOTOPEN if not.
28         
29         ifstream fileStream;
30         string receivedStringData = "";
31         
32 #ifndef WIN32
34         if (!FileExists(loadFilename)) {
35                 return CALENDAROBJECTLOAD_MISSING;
36         }
38         fileStream.open(loadFilename, ifstream::in);
40 #else
42         wstring loadFilename_utf16;
44         int len = MultiByteToWideChar(CP_UTF8, 0, &loadFilename[0], (int)loadFilename.size(), NULL, 0);
46         if (len > 0)
47         {
48                 loadFilename_utf16.resize(len);
49                 MultiByteToWideChar(CP_UTF8, 0, &loadFilename[0], (int)loadFilename.size(), &loadFilename_utf16[0], len);
50         }
52         if (!FileExists(loadFilename_utf16)) {
53                 return CALENDAROBJECTLOAD_MISSING;
54         }
56         fileStream.open(loadFilename_utf16, ifstream::in);
58 #endif
60         if (fileStream.rdstate() & ifstream::failbit){
61                 return CALENDAROBJECTLOAD_CANNOTOPEN;
62         }
64         if (fileStream.rdstate() & ifstream::badbit){
65                 return CALENDAROBJECTLOAD_CANNOTOPEN;
66         }
67         
68         // Read the data into a string.
69         
70         char *bufferRead = new char[256];
71         
72         while (!fileStream.eof()){
73                 
74                 fileStream.getline(bufferRead, 256);
75                 receivedStringData.append(bufferRead);
76                 receivedStringData.append("\n");
77                 
78         }
79         
80         delete[] bufferRead;
81         
82         CalendarObjectLoadResult stringProcResult = CALENDAROBJECTLOAD_UNITTESTFAIL;
83         
84         stringProcResult = LoadString(&receivedStringData);
85         
86         return stringProcResult;
87         
88 }
90 CalendarObjectLoadResult CalendarObject::LoadString(std::string *loadStringData){
92         bool newLine = false;
93         bool skipMode = false;
94         bool colonFound = false;
95         bool quoteMode = false;
96         char bufferChar = 0;
97         int stringDataSize = loadStringData->size();
98         int seekCount = 0;
99         string propertyName;
100         string propertyValue;
102         while (seekCount < stringDataSize){
103                 
104                 // Check if character is blank or a tab and is the first character
105                 // on a new line.
106                 
107                 if (((*loadStringData)[seekCount] == ' ' || 
108                         (*loadStringData)[seekCount] == '\t')
109                         && newLine == true){
110                         
111                         // Character is on a new line and it is a space or
112                         // tab. Ignore this character as it is not part
113                         // of the value.
115                         newLine = false;
116                                 
117                 } else if ((*loadStringData)[seekCount] == '\"'){
118                         
119                         if (quoteMode == false){
120                                 quoteMode = true;
121                         } else {
122                                 quoteMode = false;
123                         }
124                         
125                         bufferChar = (*loadStringData)[seekCount];
126                         
127                         if (colonFound == false){
128                                 propertyName += bufferChar;
129                         } else {
130                                 propertyValue += bufferChar;
131                         }
132                         
133                 } else if (newLine == true){
134                 
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
138                         // the lists.
140                         if (colonFound == true){
141                                 objectName.push_back(propertyName);
142                                 objectData.push_back(propertyValue);
143                         }
144                         
145                         colonFound = false;
146                         newLine = false;
147                         propertyName.clear();
148                         propertyValue.clear();
149                         
150                         bufferChar = (*loadStringData)[seekCount];
151                         propertyName += bufferChar;
152                         
153                 } else if ((*loadStringData)[seekCount] == '\n'){
154                 
155                         // Character is the new line character so mark
156                         // the NewLine boolean as true.
157                         
158                         newLine = true;
159                         
160                 } else if ((*loadStringData)[seekCount] == ':' &&
161                                 quoteMode == false){
162                 
163                         // Character is the colon. Set the colon
164                         // found boolen to true.
165                         
166                         bufferChar = (*loadStringData)[seekCount];
167                                         
168                         if (colonFound == true){
169                                 propertyValue += bufferChar;
170                         } else {
171                                 colonFound = true;
172                         }
173                         
174                 } else {
175                         
176                         // Character is not part of a new line and is not
177                         // the new line character itself.
178                         
179                         bufferChar = (*loadStringData)[seekCount];
180                         
181                         if (colonFound == false){
182                                 propertyName += bufferChar;
183                         } else {
184                                 propertyValue += bufferChar;
185                         }
186                         
187                 }
188                 
189                 seekCount++;
190                 
191         }
192         
193         // Finish off processing any data that wasn't processed
194         // when the end of the string was reached.
195         
196         if (colonFound == true && 
197                 propertyName.size() > 0 &&
198                 propertyValue.size() > 0){
199                 
200                 objectName.push_back(propertyName);
201                 objectData.push_back(propertyValue);
202                         
203         }
204         
205         // Check that the object contains valid data.
206         
207         CalendarObjectLoadResult stringProcResult = CALENDAROBJECTLOAD_UNITTESTFAIL;
208         CalendarObjectValidResult baseDataResult = ValidBaseObject();
209         CalendarObjectValidResult eventDataResult = ValidObject();
210         
211         if (baseDataResult != CALENDAROBJECTVALID_OK || 
212                 eventDataResult != CALENDAROBJECTVALID_OK){
213                         
214                 stringProcResult = CALENDAROBJECTLOAD_INVALIDFORMAT;
215                         
216         } else {
217                 
218                 stringProcResult = CALENDAROBJECTLOAD_OK;
219                 
220         }
221         
222         ProcessBaseData();
223         ProcessData();
224         
225         return stringProcResult;
226         
229 CalendarObjectValidResult CalendarObject::ValidBaseObject(){
230         
231         bool validBegin = false;
232         bool validAlarmBegin = false;
233         bool validVersion = false;
234         bool validEnd = false;
235         int seekCount = 0;
236         vector<int> deleteLines;
237         vector<string> alarmObjectName;
238         vector<string> alarmObjectData;
239         
240         // Check that the first line contains BEGIN:VCALENDAR
241         // and it only appears once.
242         
243         for (vector<string>::iterator iter = objectName.begin();
244                 iter != objectName.end(); iter++){
246                 if (objectName[seekCount] == "BEGIN" &&
247                         objectData[seekCount] == "VCALENDAR"){
248                         
249                         if (validBegin == false){
250                                 validBegin = true;
251                         } else {
252                                 return CALENDAROBJECTVALID_INVALIDFORMAT;
253                         }
254                                 
255                 }
256                 
257                 if (objectName[seekCount] == "END" &&
258                         objectData[seekCount] == "VCALENDAR" &&
259                         validBegin == false){
260                 
261                         return CALENDAROBJECTVALID_INVALIDFORMAT;
262                                 
263                 } else if (objectName[seekCount] == "END" &&
264                         objectData[seekCount] == "VALARM" &&
265                         validAlarmBegin == false){
267                         return CALENDAROBJECTVALID_INVALIDFORMAT;
268                                 
269                 } else if (objectName[seekCount] == "END" &&
270                         objectData[seekCount] == "VCALENDAR" &&
271                         validAlarmBegin == true){
273                         return CALENDAROBJECTVALID_INVALIDFORMAT;
274                                 
275                 }
276                 
277                 // Look for any VALARM sections.
278                 
279                 if (validAlarmBegin == true){
280                         
281                         alarmObjectName.push_back(objectName[seekCount]);
282                         alarmObjectData.push_back(objectData[seekCount]);
283                         deleteLines.push_back(seekCount);
284                         
285                 }
286                 
287                 if (objectName[seekCount] == "END" &&
288                         objectData[seekCount] == "VALARM" && 
289                         validAlarmBegin == true){
290                                 
291                         eventAlarmName.push_back(alarmObjectName);
292                         eventAlarmData.push_back(alarmObjectData);
293                         
294                         alarmObjectName.clear();
295                         alarmObjectData.clear();
297                         validAlarmBegin = false;
298                                 
299                 }
300                 
301                 if (objectName[seekCount] == "BEGIN" &&
302                         objectData[seekCount] == "VALARM" && 
303                         validBegin == true){
304                                 
305                         if (validAlarmBegin == false){
306                                 validAlarmBegin = true;
307                         } else {
308                                 return CALENDAROBJECTVALID_INVALIDFORMAT;
309                         }
310                         
311                         alarmObjectName.push_back(objectName[seekCount]);
312                         alarmObjectData.push_back(objectData[seekCount]);
313                         deleteLines.push_back(seekCount);
314                         
315                 }
316                 
317                 seekCount++;
318                         
319         }
320         
321         seekCount = 0;
322         
323         // Check that the last line contains END:VCALENDAR
324         // and it only appears once.
325         
326         for (vector<string>::iterator iter = objectName.begin();
327                 iter != objectName.end(); iter++){
328                 
329                 if (objectName[seekCount] == "END" &&
330                         objectData[seekCount] == "VCALENDAR"){
331                         
332                         if (validEnd == false){
333                                 validEnd = true;
334                         } else {
335                                 return CALENDAROBJECTVALID_INVALIDFORMAT;
336                         }
337                                 
338                 }
339                         
340                 seekCount++;
341                         
342         }
343         
344         seekCount = 0;
345         
346         // Check that the VERSION value contains 2.0 and that
347         // it only appears once.
348         
349         for (vector<string>::iterator iter = objectName.begin();
350                 iter != objectName.end(); iter++){
351                 
352                 if (objectName[seekCount] == "VERSION" &&
353                         objectData[seekCount] == "2.0"){
354                         
355                         if (validVersion == false){
356                                 validVersion = true;
357                         } else {
358                                 return CALENDAROBJECTVALID_INVALIDFORMAT;
359                         }
360                                 
361                 }
362                 
363                 seekCount++;
364                         
365         }
367         // Remove lines that aren't needed as they have
368         // been moved to the EventAlarm section.
369         
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));
375                         
376         }
378         if (validBegin == true && 
379                 validEnd == true && 
380                 validVersion == true &&
381                 validAlarmBegin == false){
382         
383                 return CALENDAROBJECTVALID_OK;
384                         
385         } else {
386                 
387                 return CALENDAROBJECTVALID_INVALIDFORMAT;
388                 
389         }
390         
393 void CalendarObject::ProcessBaseData(){
394         
395         // Process the base object data.
396         
397         multimap<string,string> dataReceived;
398         
399         // Get the method (METHOD).
400         
401         dataReceived = ProcessTextVectors(&objectName, &objectData, false, "METHOD");
402         
403         if (dataReceived.begin() != dataReceived.end()){
404                 
405                 try {
406                         methodTokens = dataReceived.begin()->first.substr(7);
407                 }
408                 
409                 catch(const out_of_range &oor){
410                         // Do nothing as there is no data.
411                 }               
412                 
413                 methodData = dataReceived.begin()->second;
414                 
415         }
416         
417         // Get the calendar scale (CALSCALE).
418         
419         dataReceived = ProcessTextVectors(&objectName, &objectData, false, "CALSCALE");
420         
421         if (dataReceived.begin() != dataReceived.end()){
422                 
423                 try {
424                         calendarScaleTokens = dataReceived.begin()->first.substr(9);
425                 }
426                 
427                 catch(const out_of_range &oor){
428                         // Do nothing as there is no data.
429                 }               
430                 
431                 calendarScaleData = dataReceived.begin()->second;
432                 
433         }
434         
Xestia Software Development
Yn Maystri
© 2006 - 2019 Xestia Software Development
Software

Xestia Address Book
Xestia Calendar
Development

Xestia Gelforn
Everything else

About
News
Privacy Policy