1
2 ///////////////////////////////////////////////////////////
3 // //
4 // SAGA //
5 // //
6 // System for Automated Geoscientific Analyses //
7 // //
8 // Application Programming Interface //
9 // //
10 // Library: SAGA_API //
11 // //
12 //-------------------------------------------------------//
13 // //
14 // table_io.cpp //
15 // //
16 // Copyright (C) 2005 by Olaf Conrad //
17 // //
18 //-------------------------------------------------------//
19 // //
20 // This file is part of 'SAGA - System for Automated //
21 // Geoscientific Analyses'. //
22 // //
23 // This library is free software; you can redistribute //
24 // it and/or modify it under the terms of the GNU Lesser //
25 // General Public License as published by the Free //
26 // Software Foundation, either version 2.1 of the //
27 // License, or (at your option) any later version. //
28 // //
29 // This library is distributed in the hope that it will //
30 // be useful, but WITHOUT ANY WARRANTY; without even the //
31 // implied warranty of MERCHANTABILITY or FITNESS FOR A //
32 // PARTICULAR PURPOSE. See the GNU Lesser General Public //
33 // License for more details. //
34 // //
35 // You should have received a copy of the GNU Lesser //
36 // General Public License along with this program; if //
37 // not, see <http://www.gnu.org/licenses/>. //
38 // //
39 //-------------------------------------------------------//
40 // //
41 // contact: Olaf Conrad //
42 // Institute of Geography //
43 // University of Goettingen //
44 // Goldschmidtstr. 5 //
45 // 37077 Goettingen //
46 // Germany //
47 // //
48 // e-mail: oconrad@saga-gis.org //
49 // //
50 ///////////////////////////////////////////////////////////
51
52 //---------------------------------------------------------
53 #include "table.h"
54 #include "table_dbase.h"
55
56
57 ///////////////////////////////////////////////////////////
58 // //
59 // //
60 // //
61 ///////////////////////////////////////////////////////////
62
63 //---------------------------------------------------------
On_Reload(void)64 bool CSG_Table::On_Reload(void)
65 {
66 return( Create(Get_File_Name(false)) );
67 }
68
69 //---------------------------------------------------------
On_Delete(void)70 bool CSG_Table::On_Delete(void)
71 {
72 return( SG_File_Delete(Get_File_Name(false)) );
73 }
74
75
76 ///////////////////////////////////////////////////////////
77 // //
78 ///////////////////////////////////////////////////////////
79
80 //---------------------------------------------------------
Set_File_Encoding(int Encoding)81 bool CSG_Table::Set_File_Encoding(int Encoding)
82 {
83 if( Encoding >= 0 && Encoding < SG_FILE_ENCODING_UNDEFINED )
84 {
85 m_Encoding = Encoding;
86
87 return( true );
88 }
89
90 return( false );
91 }
92
93
94 ///////////////////////////////////////////////////////////
95 // //
96 ///////////////////////////////////////////////////////////
97
98 //---------------------------------------------------------
Load(const CSG_String & FileName,int Format,SG_Char Separator,int Encoding)99 bool CSG_Table::Load(const CSG_String &FileName, int Format, SG_Char Separator, int Encoding)
100 {
101 Set_File_Encoding(Encoding);
102
103 if( !SG_File_Exists(FileName) )
104 {
105 return( false );
106 }
107
108 //-----------------------------------------------------
109 if( Format == TABLE_FILETYPE_Undefined )
110 {
111 Format = SG_File_Cmp_Extension(FileName, "dbf") ? TABLE_FILETYPE_DBase : TABLE_FILETYPE_Text;
112 }
113
114 if( Separator == '\0' )
115 {
116 Separator = SG_File_Cmp_Extension(FileName, "csv") ? ',' : '\t'; // comma separated values or tab spaced text
117 }
118
119 //-----------------------------------------------------
120 Destroy();
121
122 switch( Format )
123 {
124 case TABLE_FILETYPE_Text: default: if( !_Load_Text (FileName, true , Separator) ) return( false ); break;
125 case TABLE_FILETYPE_Text_NoHeadLine: if( !_Load_Text (FileName, false, Separator) ) return( false ); break;
126 case TABLE_FILETYPE_DBase : if( !_Load_DBase(FileName ) ) return( false ); break;
127 }
128
129 //-----------------------------------------------------
130 Set_Name(SG_File_Get_Name(FileName, false));
131
132 Load_MetaData(FileName);
133
134 CSG_MetaData *pFields = Get_MetaData_DB().Get_Child("FIELDS");
135
136 if( pFields && pFields->Get_Children_Count() == Get_Field_Count() )
137 {
138 for(int iField=0; iField<Get_Field_Count(); iField++)
139 {
140 Set_Field_Name(iField, pFields->Get_Content(iField));
141 }
142 }
143
144 //-----------------------------------------------------
145 return( true );
146 }
147
148 //---------------------------------------------------------
Save(const CSG_String & FileName,int Format)149 bool CSG_Table::Save(const CSG_String &FileName, int Format)
150 {
151 return( Save(FileName, Format, '\0', m_Encoding) );
152 }
153
154 //---------------------------------------------------------
Save(const CSG_String & FileName,int Format,SG_Char Separator,int Encoding)155 bool CSG_Table::Save(const CSG_String &FileName, int Format, SG_Char Separator, int Encoding)
156 {
157 SG_UI_Msg_Add(CSG_String::Format("%s %s: %s...", _TL("Saving"), _TL("table"), FileName.c_str()), true);
158
159 Set_File_Encoding(Encoding);
160
161 //-----------------------------------------------------
162 if( Format <= TABLE_FILETYPE_Undefined || Format > TABLE_FILETYPE_DBase )
163 {
164 if( SG_File_Cmp_Extension(FileName, "dbf") )
165 {
166 Format = TABLE_FILETYPE_DBase;
167 }
168 else
169 {
170 Format = TABLE_FILETYPE_Text;
171
172 if( Separator == '\0' )
173 {
174 Separator = SG_File_Cmp_Extension(FileName, "csv") ? ',' : '\t'; // comma separated values or tab spaced text
175 }
176 }
177 }
178
179 //-----------------------------------------------------
180 bool bResult = false;
181
182 switch( Format )
183 {
184 case TABLE_FILETYPE_Text: default: bResult = _Save_Text (FileName, true , Separator); break;
185 case TABLE_FILETYPE_Text_NoHeadLine: bResult = _Save_Text (FileName, false, Separator); break;
186 case TABLE_FILETYPE_DBase : bResult = _Save_DBase(FileName ); break;
187 }
188
189 //-----------------------------------------------------
190 CSG_MetaData *pFields = Get_MetaData_DB().Get_Child("FIELDS");
191
192 if( !pFields )
193 {
194 pFields = Get_MetaData_DB().Add_Child("FIELDS");
195 }
196
197 pFields->Del_Children();
198
199 for(int iField=0; iField<Get_Field_Count(); iField++)
200 {
201 pFields->Add_Child("FIELD", Get_Field_Name(iField))->Add_Property("TYPE", gSG_Data_Type_Identifier[Get_Field_Type(iField)]);
202 }
203
204 //-----------------------------------------------------
205 if( bResult )
206 {
207 Set_Modified(false);
208
209 Set_Update_Flag();
210
211 Set_File_Type(Format);
212
213 Set_File_Name(FileName, true);
214
215 Save_MetaData(FileName);
216
217 SG_UI_Msg_Add(_TL("okay"), false, SG_UI_MSG_STYLE_SUCCESS);
218
219 return( true );
220 }
221
222 SG_UI_Msg_Add(_TL("failed"), false, SG_UI_MSG_STYLE_FAILURE);
223
224 return( false );
225 }
226
227
228 ///////////////////////////////////////////////////////////
229 // //
230 // Text //
231 // //
232 ///////////////////////////////////////////////////////////
233
234 //---------------------------------------------------------
_Load_Text_Trim(CSG_String & s,const SG_Char Separator)235 size_t CSG_Table::_Load_Text_Trim(CSG_String &s, const SG_Char Separator)
236 {
237 for(size_t i=0; i<s.Length(); i++)
238 {
239 SG_Char c = s[i];
240
241 if( c == Separator || (c != ' ' && c != '\t' && c != '\n' && c != '\v' && c != '\f' && c != '\r') )
242 {
243 if( i > 0 )
244 {
245 s = s.Right(s.Length() - i);
246 }
247
248 return( i );
249 }
250 }
251
252 return( 0 );
253 }
254
255 //---------------------------------------------------------
_Load_Text_EndQuote(const CSG_String & s,const SG_Char Separator)256 size_t CSG_Table::_Load_Text_EndQuote(const CSG_String &s, const SG_Char Separator)
257 {
258 if( s.Length() > 1 && s[0] == '\"' )
259 {
260 bool bInQuotes = true;
261
262 for(size_t i=1; i<s.Length(); i++)
263 {
264 if( bInQuotes )
265 {
266 if( s[i] == '\"' )
267 {
268 bInQuotes = false;
269 }
270 }
271 else if( s[i] == '\"' )
272 {
273 bInQuotes = true;
274 }
275 else if( s[i] == Separator )
276 {
277 return( i );
278 }
279 }
280
281 if( s[s.Length() - 1] == '\"' )
282 {
283 return( s.Length() );
284 }
285 }
286
287 return( 0 );
288 }
289
290 //---------------------------------------------------------
_Load_Text(const CSG_String & FileName,bool bHeadline,const SG_Char Separator)291 bool CSG_Table::_Load_Text(const CSG_String &FileName, bool bHeadline, const SG_Char Separator)
292 {
293 CSG_File Stream;
294
295 if( Stream.Open(FileName, SG_FILE_R, false, m_Encoding) == false )
296 {
297 return( false );
298 }
299
300 sLong fLength = Stream.Length();
301
302 if( fLength <= 0 )
303 {
304 return( false );
305 }
306
307 CSG_String sLine;
308
309 if( !Stream.Read_Line(sLine) )
310 {
311 return( false );
312 }
313
314 //-----------------------------------------------------
315 CSG_Table Table;
316
317 _Load_Text_Trim(sLine, Separator);
318
319 while( !sLine.is_Empty() )
320 {
321 CSG_String sField;
322
323 if( sLine[0] == '\"' ) // value in quotas
324 {
325 sField = sLine.AfterFirst('\"').BeforeFirst('\"');
326 sLine = sLine.AfterFirst('\"').AfterFirst ('\"');
327 }
328 else
329 {
330 sField = sLine.BeforeFirst(Separator);
331 }
332
333 sLine = sLine.AfterFirst(Separator); _Load_Text_Trim(sLine, Separator);
334
335 if( !bHeadline || sField.Length() == 0 )
336 {
337 sField.Printf("F%02d", Table.Get_Field_Count() + 1);
338 }
339
340 Table.Add_Field(sField, SG_DATATYPE_String);
341 }
342
343 //-----------------------------------------------------
344 TSG_Data_Type *Type = new TSG_Data_Type[Table.Get_Field_Count()];
345
346 for(int iField=0; iField<Table.Get_Field_Count(); iField++)
347 {
348 Type[iField] = SG_DATATYPE_Int;
349 }
350
351 if( !bHeadline )
352 {
353 Stream.Seek_Start();
354 }
355
356 while( Stream.Read_Line(sLine) && SG_UI_Process_Set_Progress((double)Stream.Tell(), (double)fLength) )
357 {
358 if( sLine.Length() < 1 )
359 {
360 continue;
361 }
362
363 CSG_Table_Record *pRecord = Table.Add_Record();
364
365 _Load_Text_Trim(sLine, Separator);
366
367 for(int iField=0; iField<Table.Get_Field_Count() && !sLine.is_Empty(); iField++)
368 {
369 size_t Position = _Load_Text_EndQuote(sLine, Separator); CSG_String sField;
370
371 if( Position > 0 ) // value in quotas !!!
372 {
373 if( Position - 2 > 0 )
374 {
375 sField = sLine.Mid(1, Position - 2);
376 }
377 else
378 {
379 sField.Clear();
380 }
381
382 sLine = sLine.Right(sLine.Length() - Position);
383
384 Type[iField] = SG_DATATYPE_String;
385 }
386 else
387 {
388 sField = sLine.BeforeFirst(Separator);
389 }
390
391 sLine = sLine.AfterFirst(Separator); _Load_Text_Trim(sLine, Separator);
392
393 //---------------------------------------------
394 if( Type[iField] != SG_DATATYPE_String && !sField.is_Empty() )
395 {
396 try
397 {
398 size_t pos; double Value = std::stod(sField.to_StdString(), &pos);
399
400 if( pos < sField.Length() )
401 {
402 Type[iField] = SG_DATATYPE_String;
403 }
404 else if( Type[iField] != SG_DATATYPE_Double && (Value - (int)Value != 0. || sField.Find('.') >= 0) )
405 {
406 Type[iField] = SG_DATATYPE_Double;
407 }
408 }
409 catch(...)
410 {
411 Type[iField] = SG_DATATYPE_String;
412 }
413 }
414
415 pRecord->Set_Value(iField, sField);
416 }
417 }
418
419 //-----------------------------------------------------
420 if( Table.Get_Field_Count() > 0 )
421 {
422 for(int iField=0; iField<Table.Get_Field_Count(); iField++)
423 {
424 Add_Field(Table.Get_Field_Name(iField), Type[iField]);
425 }
426
427 for(int iRecord=0; iRecord<Table.Get_Count() && SG_UI_Process_Set_Progress(iRecord, Table.Get_Count()); iRecord++)
428 {
429 CSG_Table_Record *pRecord = Add_Record();
430
431 for(int iField=0; iField<Get_Field_Count(); iField++)
432 {
433 if( *Table[iRecord].asString(iField) )
434 {
435 pRecord->Set_Value(iField, Table[iRecord].asString(iField));
436 }
437 else
438 {
439 pRecord->Set_NoData(iField);
440 }
441 }
442 }
443 }
444
445 delete[](Type);
446
447 SG_UI_Process_Set_Ready();
448
449 return( Get_Field_Count() > 0 );
450 }
451
452 //---------------------------------------------------------
_Save_Text(const CSG_String & FileName,bool bHeadline,const SG_Char Separator)453 bool CSG_Table::_Save_Text(const CSG_String &FileName, bool bHeadline, const SG_Char Separator)
454 {
455 CSG_File Stream;
456
457 if( Get_Field_Count() <= 0 || Stream.Open(FileName, SG_FILE_W, false, m_Encoding) == false )
458 {
459 return( false );
460 }
461
462 //-----------------------------------------------------
463 for(int iField=0; iField<Get_Field_Count(); iField++)
464 {
465 Stream.Printf("%s%c", Get_Field_Name(iField), iField < Get_Field_Count() - 1 ? Separator : '\n');
466 }
467
468 //-----------------------------------------------------
469 for(int iRecord=0; iRecord<Get_Record_Count() && SG_UI_Process_Set_Progress(iRecord, Get_Record_Count()); iRecord++)
470 {
471 CSG_Table_Record *pRecord = Get_Record_byIndex(iRecord);
472
473 for(int iField=0; iField<Get_Field_Count(); iField++)
474 {
475 switch( Get_Field_Type(iField) )
476 {
477 case SG_DATATYPE_String:
478 case SG_DATATYPE_Date :
479 if( !pRecord->is_NoData(iField) )
480 {
481 Stream.Printf("\"%s\"", pRecord->asString(iField));
482 }
483 else
484 {
485 Stream.Printf("\"\"");
486 }
487 break;
488
489 default:
490 if( !pRecord->is_NoData(iField) )
491 {
492 Stream.Printf("%s", pRecord->asString(iField));
493 }
494 break;
495 }
496
497 Stream.Printf("%c", iField < Get_Field_Count() - 1 ? Separator : '\n');
498 }
499 }
500
501 //-----------------------------------------------------
502 SG_UI_Process_Set_Ready();
503
504 return( true );
505 }
506
507
508 ///////////////////////////////////////////////////////////
509 // //
510 // DBase //
511 // //
512 ///////////////////////////////////////////////////////////
513
514 //---------------------------------------------------------
_Load_DBase(const CSG_String & FileName)515 bool CSG_Table::_Load_DBase(const CSG_String &FileName)
516 {
517 CSG_Table_DBase dbf(m_Encoding);
518
519 return( dbf.Open_Read(FileName, this) );
520 }
521
522 //---------------------------------------------------------
_Save_DBase(const CSG_String & FileName)523 bool CSG_Table::_Save_DBase(const CSG_String &FileName)
524 {
525 CSG_Table_DBase dbf(m_Encoding);
526
527 return( dbf.Open_Write(FileName, this) );
528 }
529
530
531 ///////////////////////////////////////////////////////////
532 // //
533 // Serialize //
534 // //
535 ///////////////////////////////////////////////////////////
536
537 //---------------------------------------------------------
Serialize(CSG_File & Stream,bool bSave)538 bool CSG_Table::Serialize(CSG_File &Stream, bool bSave)
539 {
540 const SG_Char Separator = SG_T('\t');
541
542 //-----------------------------------------------------
543 if( bSave )
544 {
545 Stream.Printf("%d %d\n", m_nFields, m_nRecords);
546
547 for(int iField=0; iField<m_nFields; iField++)
548 {
549 Stream.Printf("%d \"%s\"\n", Get_Field_Type(iField), Get_Field_Name(iField));
550 }
551
552 for(int iRecord=0; iRecord<m_nRecords; iRecord++)
553 {
554 for(int iField=0; iField<m_nFields; iField++)
555 {
556 Stream.Printf("%s%c", Get_Record(iRecord)->asString(iField), iField < m_nFields - 1 ? Separator : '\n');
557 }
558 }
559
560 return( true );
561 }
562
563 //-----------------------------------------------------
564 CSG_String sLine; int nFields, nRecords, FieldType;
565
566 if( Stream.Read_Line(sLine) && SG_SSCANF(sLine, SG_T("%d %d"), &nFields, &nRecords) == 2 && nFields > 0 )
567 {
568 Destroy();
569
570 for(int iField=0; iField<nFields; iField++)
571 {
572 if( Stream.Read_Line(sLine) && SG_SSCANF(sLine, SG_T("%d"), &FieldType) == 1 )
573 {
574 Add_Field(sLine.AfterFirst('\"').BeforeFirst('\"'), (TSG_Data_Type)FieldType);
575 }
576 }
577
578 for(int iRecord=0; iRecord<nRecords; iRecord++)
579 {
580 if( Stream.Read_Line(sLine) )
581 {
582 CSG_Table_Record *pRecord = Add_Record();
583
584 for(int iField=0; iField<m_nFields; iField++)
585 {
586 pRecord->Set_Value(iField, sLine.BeforeFirst(Separator));
587
588 sLine = sLine.AfterFirst(Separator);
589 }
590 }
591 }
592
593 return( true );
594 }
595
596 //-----------------------------------------------------
597 return( false );
598 }
599
600
601 ///////////////////////////////////////////////////////////
602 // //
603 // //
604 // //
605 ///////////////////////////////////////////////////////////
606
607 //---------------------------------------------------------
608