1 /******************************************************************************
2  *
3  * Project:  XLS Translator
4  * Purpose:  Implements OGRXLSLayer class.
5  * Author:   Even Rouault, <even dot rouault at spatialys.com>
6  *
7  ******************************************************************************
8  * Copyright (c) 2011-2012, Even Rouault <even dot rouault at spatialys.com>
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  ****************************************************************************/
28 
29 #include <freexl.h>
30 
31 #include "ogr_xls.h"
32 #include "cpl_conv.h"
33 #include "cpl_string.h"
34 
35 CPL_CVSID("$Id: ogrxlslayer.cpp 08fa620992e06a57dd580ce9ff53b3a9fcd05682 2021-07-16 12:31:42 +0200 Even Rouault $")
36 
37 /************************************************************************/
38 /*                            OGRXLSLayer()                             */
39 /************************************************************************/
40 
OGRXLSLayer(OGRXLSDataSource * poDSIn,const char * pszSheetname,int iSheetIn,int nRowsIn,unsigned short nColsIn)41 OGRXLSLayer::OGRXLSLayer( OGRXLSDataSource* poDSIn,
42                           const char* pszSheetname,
43                           int iSheetIn,
44                           int nRowsIn,
45                           unsigned short nColsIn ) :
46     poDS(poDSIn),
47     poFeatureDefn(nullptr),
48     pszName(CPLStrdup(pszSheetname)),
49     iSheet(iSheetIn),
50     bFirstLineIsHeaders(false),
51     nRows(nRowsIn),
52     nCols(nColsIn),
53     nNextFID(0)
54 {
55     SetDescription( pszName );
56 }
57 
58 /************************************************************************/
59 /*                            ~OGRXLSLayer()                            */
60 /************************************************************************/
61 
~OGRXLSLayer()62 OGRXLSLayer::~OGRXLSLayer()
63 
64 {
65     CPLFree(pszName);
66     if( poFeatureDefn )
67         poFeatureDefn->Release();
68 }
69 
70 /************************************************************************/
71 /*                            ResetReading()                            */
72 /************************************************************************/
73 
ResetReading()74 void OGRXLSLayer::ResetReading()
75 
76 {
77     if (poFeatureDefn != nullptr)
78     {
79         nNextFID = bFirstLineIsHeaders ? 1 : 0;
80     }
81 }
82 
83 /************************************************************************/
84 /*                          DetectHeaderLine()                          */
85 /************************************************************************/
86 
DetectHeaderLine(const void * xlshandle)87 void OGRXLSLayer::DetectHeaderLine(const void* xlshandle)
88 
89 {
90     FreeXL_CellValue sCellValue;
91     int nCountTextOnSecondLine = 0;
92     unsigned short i = 0;  // Used after for.
93     for( ; i < nCols && nRows >= 2; i++ )
94     {
95         if (freexl_get_cell_value(xlshandle, 0, i, &sCellValue) == FREEXL_OK)
96         {
97             if (sCellValue.type != FREEXL_CELL_TEXT &&
98                 sCellValue.type != FREEXL_CELL_SST_TEXT)
99             {
100                 /* If the values in the first line are not text, then it is */
101                 /* not a header line */
102                 break;
103             }
104         }
105         if (freexl_get_cell_value(xlshandle, 1, i, &sCellValue) == FREEXL_OK)
106         {
107             if (sCellValue.type == FREEXL_CELL_TEXT ||
108                 sCellValue.type == FREEXL_CELL_SST_TEXT)
109             {
110                 /* If there are only text values on the second line, then we cannot */
111                 /* know if it is a header line or just a regular line */
112                 nCountTextOnSecondLine ++;
113             }
114         }
115     }
116 
117     const char* pszXLSHeaders = CPLGetConfigOption("OGR_XLS_HEADERS", "");
118     if( EQUAL(pszXLSHeaders, "FORCE") )
119         bFirstLineIsHeaders = true;
120     else if( EQUAL(pszXLSHeaders, "DISABLE") )
121         bFirstLineIsHeaders = false;
122     else if( i == nCols && nCountTextOnSecondLine != nCols )
123         bFirstLineIsHeaders = true;
124 }
125 
126 /************************************************************************/
127 /*                         DetectColumnTypes()                          */
128 /************************************************************************/
129 
DetectColumnTypes(const void * xlshandle,int * paeFieldTypes)130 void OGRXLSLayer::DetectColumnTypes(const void* xlshandle,
131                                     int* paeFieldTypes)
132 
133 {
134     FreeXL_CellValue sCellValue;
135     for( int j = bFirstLineIsHeaders ? 1 : 0; j < nRows; j++ )
136     {
137         for( unsigned short i = 0; i < nCols; i ++)
138         {
139             if (freexl_get_cell_value(xlshandle, j, i, &sCellValue) == FREEXL_OK)
140             {
141                 int eType = paeFieldTypes[i];
142                 switch (sCellValue.type)
143                 {
144                     case FREEXL_CELL_INT:
145                         eType = OFTInteger;
146                         break;
147                     case FREEXL_CELL_DOUBLE:
148                         eType = OFTReal;
149                         break;
150                     case FREEXL_CELL_TEXT:
151                     case FREEXL_CELL_SST_TEXT:
152                         eType = OFTString;
153                         break;
154                     case FREEXL_CELL_DATE:
155                         eType = OFTDate;
156                         break;
157                     case FREEXL_CELL_DATETIME:
158                         eType = OFTDateTime;
159                         break;
160                     case FREEXL_CELL_TIME:
161                         eType = OFTTime;
162                         break;
163                     case FREEXL_CELL_NULL:
164                         break;
165                     default:
166                         break;
167                 }
168 
169                 if (paeFieldTypes[i] < 0)
170                 {
171                     paeFieldTypes[i] = eType;
172                 }
173                 else if (eType != paeFieldTypes[i])
174                 {
175                     if ((paeFieldTypes[i] == OFTDate ||
176                          paeFieldTypes[i] == OFTTime ||
177                          paeFieldTypes[i] == OFTDateTime) &&
178                         (eType == OFTDate || eType == OFTTime || eType == OFTDateTime))
179                         paeFieldTypes[i] = OFTDateTime;
180                     else if (paeFieldTypes[i] == OFTReal && eType == OFTInteger)
181                         /* nothing */ ;
182                     else if (paeFieldTypes[i] == OFTInteger && eType == OFTReal)
183                         paeFieldTypes[i] = OFTReal;
184                     else
185                         paeFieldTypes[i] = OFTString;
186                 }
187             }
188         }
189     }
190 }
191 
192 /************************************************************************/
193 /*                            GetLayerDefn()                            */
194 /************************************************************************/
195 
GetLayerDefn()196 OGRFeatureDefn * OGRXLSLayer::GetLayerDefn()
197 {
198     if (poFeatureDefn)
199         return poFeatureDefn;
200 
201     poFeatureDefn = new OGRFeatureDefn( pszName );
202     poFeatureDefn->Reference();
203     poFeatureDefn->SetGeomType( wkbNone );
204 
205     const void* xlshandle = poDS->GetXLSHandle();
206     if (xlshandle == nullptr)
207         return poFeatureDefn;
208 
209     freexl_select_active_worksheet(xlshandle, (unsigned short)iSheet);
210 
211     if (nRows > 0)
212     {
213 
214         FreeXL_CellValue sCellValue;
215 
216         DetectHeaderLine(xlshandle);
217 
218         int* paeFieldTypes = (int* )
219                             CPLMalloc(nCols * sizeof(int));
220         for( unsigned short i = 0; i < nCols; i++ )
221         {
222             paeFieldTypes[i] = -1;
223         }
224 
225         const char* pszXLSFieldTypes =
226                         CPLGetConfigOption("OGR_XLS_FIELD_TYPES", "");
227         if (!EQUAL(pszXLSFieldTypes, "STRING"))
228             DetectColumnTypes(xlshandle, paeFieldTypes);
229 
230         for( unsigned short i = 0; i < nCols; i++ )
231         {
232             OGRFieldType eType = (OGRFieldType) paeFieldTypes[i];
233             if (paeFieldTypes[i] < 0)
234                 eType = OFTString;
235             if( bFirstLineIsHeaders &&
236                 freexl_get_cell_value(xlshandle,
237                                       0, i, &sCellValue) == FREEXL_OK &&
238                 (sCellValue.type == FREEXL_CELL_TEXT ||
239                  sCellValue.type == FREEXL_CELL_SST_TEXT) )
240             {
241                 OGRFieldDefn oField(sCellValue.value.text_value, eType);
242                 poFeatureDefn->AddFieldDefn(&oField);
243             }
244             else
245             {
246                 OGRFieldDefn oField(CPLSPrintf("Field%d", i+1),  eType);
247                 poFeatureDefn->AddFieldDefn(&oField);
248             }
249         }
250 
251         CPLFree(paeFieldTypes);
252     }
253 
254     ResetReading();
255 
256     return poFeatureDefn;
257 }
258 
259 /************************************************************************/
260 /*                          GetFeatureCount()                           */
261 /************************************************************************/
262 
GetFeatureCount(int bForce)263 GIntBig OGRXLSLayer::GetFeatureCount( int bForce )
264 {
265     if( m_poAttrQuery == nullptr /* && m_poFilterGeom == NULL */ )
266     {
267         const char* pszXLSHeaders = CPLGetConfigOption("OGR_XLS_HEADERS", "");
268         if(EQUAL(pszXLSHeaders, "DISABLE"))
269             return nRows;
270 
271         GetLayerDefn();
272         return bFirstLineIsHeaders ? nRows - 1 : nRows;
273     }
274 
275     return OGRLayer::GetFeatureCount(bForce);
276 }
277 
278 /************************************************************************/
279 /*                         GetNextRawFeature()                          */
280 /************************************************************************/
281 
GetNextRawFeature()282 OGRFeature *OGRXLSLayer::GetNextRawFeature()
283 {
284     GetLayerDefn();
285 
286     if (nNextFID == nRows)
287         return nullptr;
288 
289     const void* xlshandle = poDS->GetXLSHandle();
290     if (xlshandle == nullptr)
291         return nullptr;
292 
293     freexl_select_active_worksheet(xlshandle, (unsigned short)iSheet);
294 
295     OGRFeature* poFeature = new OGRFeature(poFeatureDefn);
296 
297     FreeXL_CellValue sCellValue;
298     for(unsigned short i=0;i<(unsigned short )poFeatureDefn->GetFieldCount(); i++)
299     {
300         if (freexl_get_cell_value(xlshandle, nNextFID, i, &sCellValue) == FREEXL_OK)
301         {
302             switch (sCellValue.type)
303             {
304                 case FREEXL_CELL_INT:
305                     poFeature->SetField(i, sCellValue.value.int_value);
306                     break;
307                 case FREEXL_CELL_DOUBLE:
308                     poFeature->SetField(i, sCellValue.value.double_value);
309                     break;
310                 case FREEXL_CELL_TEXT:
311                 case FREEXL_CELL_SST_TEXT:
312                     poFeature->SetField(i, sCellValue.value.text_value);
313                     break;
314                 case FREEXL_CELL_DATE:
315                 case FREEXL_CELL_DATETIME:
316                 case FREEXL_CELL_TIME:
317                     poFeature->SetField(i, sCellValue.value.text_value);
318                     break;
319                 case FREEXL_CELL_NULL:
320                     break;
321                 default:
322                     CPLDebug("XLS", "Unknown cell type = %d", sCellValue.type);
323                     break;
324             }
325         }
326     }
327 
328     poFeature->SetFID(nNextFID + 1);
329     nNextFID ++;
330 
331     return poFeature;
332 }
333 
334 /************************************************************************/
335 /*                           TestCapability()                           */
336 /************************************************************************/
337 
TestCapability(const char * pszCap)338 int OGRXLSLayer::TestCapability( const char * pszCap )
339 
340 {
341     if( EQUAL(pszCap, OLCFastFeatureCount) )
342         return m_poAttrQuery == nullptr /* && m_poFilterGeom == NULL */;
343     else if( EQUAL(pszCap, OLCStringsAsUTF8) )
344         return TRUE;
345 
346     return FALSE;
347 }
348