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