1 : /******************************************************************************
2 : * $Id: ogrcsvdatasource.cpp 17806 2009-10-13 17:27:54Z rouault $
3 : *
4 : * Project: CSV Translator
5 : * Purpose: Implements OGRCSVDataSource class
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "ogr_csv.h"
31 : #include "cpl_conv.h"
32 : #include "cpl_string.h"
33 : #include "cpl_csv.h"
34 :
35 : CPL_CVSID("$Id: ogrcsvdatasource.cpp 17806 2009-10-13 17:27:54Z rouault $");
36 :
37 : /************************************************************************/
38 : /* OGRCSVDataSource() */
39 : /************************************************************************/
40 :
41 164 : OGRCSVDataSource::OGRCSVDataSource()
42 :
43 : {
44 164 : papoLayers = NULL;
45 164 : nLayers = 0;
46 :
47 164 : pszName = NULL;
48 :
49 164 : bUpdate = FALSE;
50 164 : }
51 :
52 : /************************************************************************/
53 : /* ~OGRCSVDataSource() */
54 : /************************************************************************/
55 :
56 328 : OGRCSVDataSource::~OGRCSVDataSource()
57 :
58 : {
59 214 : for( int i = 0; i < nLayers; i++ )
60 50 : delete papoLayers[i];
61 164 : CPLFree( papoLayers );
62 :
63 164 : CPLFree( pszName );
64 328 : }
65 :
66 : /************************************************************************/
67 : /* TestCapability() */
68 : /************************************************************************/
69 :
70 4 : int OGRCSVDataSource::TestCapability( const char * pszCap )
71 :
72 : {
73 4 : if( EQUAL(pszCap,ODsCCreateLayer) )
74 2 : return bUpdate;
75 2 : else if( EQUAL(pszCap,ODsCDeleteLayer) )
76 2 : return bUpdate;
77 : else
78 0 : return FALSE;
79 : }
80 :
81 : /************************************************************************/
82 : /* GetLayer() */
83 : /************************************************************************/
84 :
85 51 : OGRLayer *OGRCSVDataSource::GetLayer( int iLayer )
86 :
87 : {
88 51 : if( iLayer < 0 || iLayer >= nLayers )
89 0 : return NULL;
90 : else
91 51 : return papoLayers[iLayer];
92 : }
93 :
94 : /************************************************************************/
95 : /* Open() */
96 : /************************************************************************/
97 :
98 164 : int OGRCSVDataSource::Open( const char * pszFilename, int bUpdateIn,
99 : int bForceOpen )
100 :
101 : {
102 164 : pszName = CPLStrdup( pszFilename );
103 164 : bUpdate = bUpdateIn;
104 :
105 : /* -------------------------------------------------------------------- */
106 : /* Determine what sort of object this is. */
107 : /* -------------------------------------------------------------------- */
108 : VSIStatBufL sStatBuf;
109 :
110 164 : if( VSIStatL( pszFilename, &sStatBuf ) != 0 )
111 60 : return FALSE;
112 :
113 : /* -------------------------------------------------------------------- */
114 : /* Is this a single CSV file? */
115 : /* -------------------------------------------------------------------- */
116 104 : if( VSI_ISREG(sStatBuf.st_mode)
117 : && strlen(pszFilename) > 4
118 : && EQUAL(pszFilename+strlen(pszFilename)-4,".csv") )
119 20 : return OpenTable( pszFilename );
120 :
121 : /* -------------------------------------------------------------------- */
122 : /* Otherwise it has to be a directory. */
123 : /* -------------------------------------------------------------------- */
124 84 : if( !VSI_ISDIR(sStatBuf.st_mode) )
125 76 : return FALSE;
126 :
127 : /* -------------------------------------------------------------------- */
128 : /* Scan through for entries ending in .csv. */
129 : /* -------------------------------------------------------------------- */
130 8 : int nNotCSVCount = 0, i;
131 8 : char **papszNames = CPLReadDir( pszFilename );
132 :
133 8 : for( i = 0; papszNames != NULL && papszNames[i] != NULL; i++ )
134 : {
135 : CPLString oSubFilename =
136 60 : CPLFormFilename( pszFilename, papszNames[i], NULL );
137 :
138 60 : if( EQUAL(papszNames[i],".") || EQUAL(papszNames[i],"..") )
139 16 : continue;
140 :
141 44 : if (EQUAL(CPLGetExtension(oSubFilename),"csvt"))
142 13 : continue;
143 :
144 31 : if( VSIStatL( oSubFilename, &sStatBuf ) != 0
145 : || !VSI_ISREG(sStatBuf.st_mode)
146 : || !EQUAL(CPLGetExtension(oSubFilename),"csv") )
147 : {
148 8 : nNotCSVCount++;
149 8 : continue;
150 : }
151 :
152 23 : if( !OpenTable( oSubFilename ) )
153 : {
154 0 : CSLDestroy( papszNames );
155 0 : nNotCSVCount++;
156 0 : return FALSE;
157 : }
158 : }
159 :
160 8 : CSLDestroy( papszNames );
161 :
162 : /* -------------------------------------------------------------------- */
163 : /* We presume that this is indeed intended to be a CSV */
164 : /* datasource if over half the files were .csv files. */
165 : /* -------------------------------------------------------------------- */
166 8 : return bForceOpen || nNotCSVCount < nLayers;
167 : }
168 :
169 : /************************************************************************/
170 : /* OpenTable() */
171 : /************************************************************************/
172 :
173 43 : int OGRCSVDataSource::OpenTable( const char * pszFilename )
174 :
175 : {
176 : /* -------------------------------------------------------------------- */
177 : /* Open the file. */
178 : /* -------------------------------------------------------------------- */
179 : FILE * fp;
180 :
181 43 : if( bUpdate )
182 14 : fp = VSIFOpen( pszFilename, "rb+" );
183 : else
184 29 : fp = VSIFOpen( pszFilename, "rb" );
185 43 : if( fp == NULL )
186 : {
187 : CPLError( CE_Warning, CPLE_OpenFailed,
188 : "Failed to open %s, %s.",
189 0 : pszFilename, VSIStrerror( errno ) );
190 0 : return FALSE;
191 : }
192 :
193 : /* -------------------------------------------------------------------- */
194 : /* Read and parse a line. Did we get multiple fields? */
195 : /* -------------------------------------------------------------------- */
196 :
197 43 : const char* pszLine = CPLReadLine( fp );
198 43 : if (pszLine == NULL)
199 : {
200 0 : VSIFClose( fp );
201 0 : return FALSE;
202 : }
203 43 : char chDelimiter = CSVDetectSeperator(pszLine);
204 43 : VSIRewind( fp );
205 :
206 43 : char **papszFields = CSVReadParseLine2( fp, chDelimiter );
207 :
208 43 : if( CSLCount(papszFields) < 2 )
209 : {
210 0 : VSIFClose( fp );
211 0 : CSLDestroy( papszFields );
212 0 : return FALSE;
213 : }
214 :
215 43 : VSIRewind( fp );
216 43 : CSLDestroy( papszFields );
217 :
218 : /* -------------------------------------------------------------------- */
219 : /* Create a layer. */
220 : /* -------------------------------------------------------------------- */
221 43 : nLayers++;
222 : papoLayers = (OGRCSVLayer **) CPLRealloc(papoLayers,
223 43 : sizeof(void*) * nLayers);
224 :
225 43 : papoLayers[nLayers-1] =
226 86 : new OGRCSVLayer( CPLGetBasename(pszFilename), fp, pszFilename, FALSE, bUpdate, chDelimiter );
227 :
228 43 : return TRUE;
229 : }
230 :
231 : /************************************************************************/
232 : /* CreateLayer() */
233 : /************************************************************************/
234 :
235 : OGRLayer *
236 8 : OGRCSVDataSource::CreateLayer( const char *pszLayerName,
237 : OGRSpatialReference *poSpatialRef,
238 : OGRwkbGeometryType eGType,
239 : char ** papszOptions )
240 :
241 : {
242 : /* -------------------------------------------------------------------- */
243 : /* Verify we are in update mode. */
244 : /* -------------------------------------------------------------------- */
245 8 : if (!bUpdate)
246 : {
247 : CPLError( CE_Failure, CPLE_NoWriteAccess,
248 : "Data source %s opened read-only.\n"
249 : "New layer %s cannot be created.\n",
250 0 : pszName, pszLayerName );
251 :
252 0 : return NULL;
253 : }
254 :
255 : /* -------------------------------------------------------------------- */
256 : /* Verify that the datasource is a directory. */
257 : /* -------------------------------------------------------------------- */
258 : VSIStatBuf sStatBuf;
259 :
260 8 : if( VSIStat( pszName, &sStatBuf ) != 0
261 : || !VSI_ISDIR( sStatBuf.st_mode ) )
262 : {
263 : CPLError( CE_Failure, CPLE_AppDefined,
264 0 : "Attempt to create csv layer (file) against a non-directory datasource." );
265 0 : return NULL;
266 : }
267 :
268 : /* -------------------------------------------------------------------- */
269 : /* What filename would we use? */
270 : /* -------------------------------------------------------------------- */
271 : const char *pszFilename;
272 :
273 8 : pszFilename = CPLFormFilename( pszName, pszLayerName, "csv" );
274 :
275 : /* -------------------------------------------------------------------- */
276 : /* does this file already exist? */
277 : /* -------------------------------------------------------------------- */
278 :
279 8 : if( VSIStat( pszName, &sStatBuf ) != 0 )
280 : {
281 : CPLError( CE_Failure, CPLE_AppDefined,
282 : "Attempt to create layer %s, but file %s already exists.",
283 0 : pszLayerName, pszFilename );
284 0 : return NULL;
285 : }
286 :
287 : /* -------------------------------------------------------------------- */
288 : /* Create the empty file. */
289 : /* -------------------------------------------------------------------- */
290 : FILE *fp;
291 :
292 8 : fp = VSIFOpen( pszFilename, "w+b" );
293 :
294 8 : if( fp == NULL )
295 : {
296 : CPLError( CE_Failure, CPLE_OpenFailed,
297 : "Failed to create %s:\n%s",
298 0 : pszFilename, VSIStrerror( errno ) );
299 :
300 :
301 0 : return NULL;
302 : }
303 :
304 :
305 8 : const char *pszDelimiter = CSLFetchNameValue( papszOptions, "SEPARATOR");
306 8 : char chDelimiter = ',';
307 8 : if (pszDelimiter != NULL)
308 : {
309 1 : if (EQUAL(pszDelimiter, "COMMA"))
310 0 : chDelimiter = ',';
311 1 : else if (EQUAL(pszDelimiter, "SEMICOLON"))
312 1 : chDelimiter = ';';
313 0 : else if (EQUAL(pszDelimiter, "TAB"))
314 0 : chDelimiter = '\t';
315 : else
316 : {
317 : CPLError( CE_Warning, CPLE_AppDefined,
318 : "SEPARATOR=%s not understood, use one of COMMA, SEMICOLON or TAB.",
319 0 : pszDelimiter );
320 : }
321 : }
322 :
323 : /* -------------------------------------------------------------------- */
324 : /* Create a layer. */
325 : /* -------------------------------------------------------------------- */
326 8 : nLayers++;
327 : papoLayers = (OGRCSVLayer **) CPLRealloc(papoLayers,
328 8 : sizeof(void*) * nLayers);
329 :
330 8 : papoLayers[nLayers-1] = new OGRCSVLayer( pszLayerName, fp, pszFilename, TRUE, TRUE, chDelimiter );
331 :
332 : /* -------------------------------------------------------------------- */
333 : /* Was a partiuclar CRLF order requested? */
334 : /* -------------------------------------------------------------------- */
335 8 : const char *pszCRLFFormat = CSLFetchNameValue( papszOptions, "LINEFORMAT");
336 : int bUseCRLF;
337 :
338 8 : if( pszCRLFFormat == NULL )
339 : {
340 : #ifdef WIN32
341 : bUseCRLF = TRUE;
342 : #else
343 7 : bUseCRLF = FALSE;
344 : #endif
345 : }
346 1 : else if( EQUAL(pszCRLFFormat,"CRLF") )
347 1 : bUseCRLF = TRUE;
348 0 : else if( EQUAL(pszCRLFFormat,"LF") )
349 0 : bUseCRLF = FALSE;
350 : else
351 : {
352 : CPLError( CE_Warning, CPLE_AppDefined,
353 : "LINEFORMAT=%s not understood, use one of CRLF or LF.",
354 0 : pszCRLFFormat );
355 : #ifdef WIN32
356 : bUseCRLF = TRUE;
357 : #else
358 0 : bUseCRLF = FALSE;
359 : #endif
360 : }
361 :
362 8 : papoLayers[nLayers-1]->SetCRLF( bUseCRLF );
363 :
364 : /* -------------------------------------------------------------------- */
365 : /* Should we write the geometry ? */
366 : /* -------------------------------------------------------------------- */
367 8 : const char *pszGeometry = CSLFetchNameValue( papszOptions, "GEOMETRY");
368 8 : if (pszGeometry != NULL)
369 : {
370 4 : if (EQUAL(pszGeometry, "AS_WKT"))
371 : {
372 1 : papoLayers[nLayers-1]->SetWriteGeometry(OGR_CSV_GEOM_AS_WKT);
373 : }
374 6 : else if (EQUAL(pszGeometry, "AS_XYZ") ||
375 : EQUAL(pszGeometry, "AS_XY") ||
376 : EQUAL(pszGeometry, "AS_YX"))
377 : {
378 6 : if (eGType == wkbUnknown || wkbFlatten(eGType) == wkbPoint)
379 : {
380 3 : papoLayers[nLayers-1]->SetWriteGeometry(EQUAL(pszGeometry, "AS_XYZ") ? OGR_CSV_GEOM_AS_XYZ :
381 : EQUAL(pszGeometry, "AS_XY") ? OGR_CSV_GEOM_AS_XY :
382 6 : OGR_CSV_GEOM_AS_YX);
383 : }
384 : else
385 : {
386 : CPLError( CE_Warning, CPLE_AppDefined,
387 : "Geometry type %s is not compatible with GEOMETRY=AS_XYZ.",
388 0 : OGRGeometryTypeToName(eGType) );
389 : }
390 : }
391 : else
392 : {
393 : CPLError( CE_Warning, CPLE_AppDefined,
394 : "Unsupported value %s for creation option GEOMETRY",
395 0 : pszGeometry );
396 : }
397 : }
398 :
399 : /* -------------------------------------------------------------------- */
400 : /* Should we create a CSVT file ? */
401 : /* -------------------------------------------------------------------- */
402 :
403 8 : const char *pszCreateCSVT = CSLFetchNameValue( papszOptions, "CREATE_CSVT");
404 8 : if (pszCreateCSVT)
405 4 : papoLayers[nLayers-1]->SetCreateCSVT(CSLTestBoolean(pszCreateCSVT));
406 :
407 8 : return papoLayers[nLayers-1];
408 : }
409 :
410 : /************************************************************************/
411 : /* DeleteLayer() */
412 : /************************************************************************/
413 :
414 1 : OGRErr OGRCSVDataSource::DeleteLayer( int iLayer )
415 :
416 : {
417 : char *pszFilename;
418 : char *pszFilenameCSVT;
419 :
420 : /* -------------------------------------------------------------------- */
421 : /* Verify we are in update mode. */
422 : /* -------------------------------------------------------------------- */
423 1 : if( !bUpdate )
424 : {
425 : CPLError( CE_Failure, CPLE_NoWriteAccess,
426 : "Data source %s opened read-only.\n"
427 : "Layer %d cannot be deleted.\n",
428 0 : pszName, iLayer );
429 :
430 0 : return OGRERR_FAILURE;
431 : }
432 :
433 1 : if( iLayer < 0 || iLayer >= nLayers )
434 : {
435 : CPLError( CE_Failure, CPLE_AppDefined,
436 : "Layer %d not in legal range of 0 to %d.",
437 0 : iLayer, nLayers-1 );
438 0 : return OGRERR_FAILURE;
439 : }
440 :
441 : pszFilename =
442 1 : CPLStrdup(CPLFormFilename(pszName,papoLayers[iLayer]->GetLayerDefn()->GetName(),"csv"));
443 : pszFilenameCSVT =
444 1 : CPLStrdup(CPLFormFilename(pszName,papoLayers[iLayer]->GetLayerDefn()->GetName(),"csvt"));
445 :
446 1 : delete papoLayers[iLayer];
447 :
448 3 : while( iLayer < nLayers - 1 )
449 : {
450 1 : papoLayers[iLayer] = papoLayers[iLayer+1];
451 1 : iLayer++;
452 : }
453 :
454 1 : nLayers--;
455 :
456 1 : VSIUnlink( pszFilename );
457 1 : CPLFree( pszFilename );
458 1 : VSIUnlink( pszFilenameCSVT );
459 1 : CPLFree( pszFilenameCSVT );
460 :
461 1 : return OGRERR_NONE;
462 : }
|