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