1 : /**********************************************************************
2 : * $Id: mitab_tabview.cpp,v 1.22 2010-07-07 19:00:15 aboudreault Exp $
3 : *
4 : * Name: mitab_tabfile.cpp
5 : * Project: MapInfo TAB Read/Write library
6 : * Language: C++
7 : * Purpose: Implementation of the TABView class, used to handle .TAB
8 : * datasets composed of a number of .TAB files linked through
9 : * indexed fields.
10 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
11 : *
12 : **********************************************************************
13 : * Copyright (c) 1999-2002, Daniel Morissette
14 : *
15 : * Permission is hereby granted, free of charge, to any person obtaining a
16 : * copy of this software and associated documentation files (the "Software"),
17 : * to deal in the Software without restriction, including without limitation
18 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
19 : * and/or sell copies of the Software, and to permit persons to whom the
20 : * Software is furnished to do so, subject to the following conditions:
21 : *
22 : * The above copyright notice and this permission notice shall be included
23 : * in all copies or substantial portions of the Software.
24 : *
25 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
28 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31 : * DEALINGS IN THE SOFTWARE.
32 : **********************************************************************
33 : *
34 : * $Log: mitab_tabview.cpp,v $
35 : * Revision 1.22 2010-07-07 19:00:15 aboudreault
36 : * Cleanup Win32 Compile Warnings (GDAL bug #2930)
37 : *
38 : * Revision 1.21 2010-07-05 19:01:20 aboudreault
39 : * Reverted last SetFeature change in mitab_capi.cpp and fixed another memory leak
40 : *
41 : * Revision 1.20 2010-01-07 20:39:12 aboudreault
42 : * Added support to handle duplicate field names, Added validation to check if a field name start with a number (bug 2141)
43 : *
44 : * Revision 1.19 2008-03-05 20:35:39 dmorissette
45 : * Replace MITAB 1.x SetFeature() with a CreateFeature() for V2.x (bug 1859)
46 : *
47 : * Revision 1.18 2008/01/29 20:46:32 dmorissette
48 : * Added support for v9 Time and DateTime fields (byg 1754)
49 : *
50 : * Revision 1.17 2007/06/21 14:00:23 dmorissette
51 : * Added missing cast in isspace() calls to avoid failed assertion on Windows
52 : * (MITAB bug 1737, GDAL ticket 1678))
53 : *
54 : * Revision 1.16 2007/06/12 13:52:38 dmorissette
55 : * Added IMapInfoFile::SetCharset() method (bug 1734)
56 : *
57 : * Revision 1.15 2007/06/12 12:50:40 dmorissette
58 : * Use Quick Spatial Index by default until bug 1732 is fixed (broken files
59 : * produced by current coord block splitting technique).
60 : *
61 : * Revision 1.14 2007/03/21 21:15:56 dmorissette
62 : * Added SetQuickSpatialIndexMode() which generates a non-optimal spatial
63 : * index but results in faster write time (bug 1669)
64 : *
65 : * Revision 1.13 2004/06/30 20:29:04 dmorissette
66 : * Fixed refs to old address danmo@videotron.ca
67 : *
68 : * Revision 1.12 2002/02/22 20:44:51 julien
69 : * Prevent infinite loop with TABRelation by suppress the m_poCurFeature object
70 : * from the class and setting it in the calling function and add GetFeature in
71 : * the class. (bug 706)
72 : *
73 : * Revision 1.11 2002/01/10 05:13:22 daniel
74 : * Prevent crash if .IND file is deleted (but 703)
75 : *
76 : * Revision 1.10 2002/01/10 04:52:58 daniel
77 : * Support 'select * ...' syntax + 'open table..." directives with/without .tab
78 : *
79 : * Revision 1.9 2001/06/27 19:52:26 warmerda
80 : * use VSIUnlink() instead of unlink()
81 : *
82 : * Revision 1.8 2001/03/15 03:57:51 daniel
83 : * Added implementation for new OGRLayer::GetExtent(), returning data MBR.
84 : *
85 : * Revision 1.7 2000/09/28 16:39:44 warmerda
86 : * avoid warnings for unused, and unitialized variables
87 : *
88 : * Revision 1.6 2000/02/28 17:12:22 daniel
89 : * Write support for joined tables and indexed fields
90 : *
91 : * Revision 1.5 2000/01/15 22:30:45 daniel
92 : * Switch to MIT/X-Consortium OpenSource license
93 : *
94 : * Revision 1.4 1999/12/19 17:40:16 daniel
95 : * Init + delete m_poRelation properly
96 : *
97 : * Revision 1.3 1999/12/14 05:53:00 daniel
98 : * Fixed compile warnings
99 : *
100 : * Revision 1.2 1999/12/14 04:04:10 daniel
101 : * Added bforceFlags to GetBounds() and GetFeatureCountByType()
102 : *
103 : * Revision 1.1 1999/12/14 02:10:32 daniel
104 : * Initial revision
105 : *
106 : **********************************************************************/
107 :
108 : #include "mitab.h"
109 : #include "mitab_utils.h"
110 :
111 : #include <ctype.h> /* isspace() */
112 :
113 : /*=====================================================================
114 : * class TABView
115 : *====================================================================*/
116 :
117 :
118 : /**********************************************************************
119 : * TABView::TABView()
120 : *
121 : * Constructor.
122 : **********************************************************************/
123 0 : TABView::TABView()
124 : {
125 0 : m_pszFname = NULL;
126 0 : m_eAccessMode = TABRead;
127 0 : m_papszTABFile = NULL;
128 0 : m_pszVersion = NULL;
129 :
130 0 : m_numTABFiles = 0;
131 0 : m_papszTABFnames = NULL;
132 0 : m_papoTABFiles = NULL;
133 0 : m_nMainTableIndex = -1;
134 :
135 0 : m_papszFieldNames = NULL;
136 0 : m_papszWhereClause = NULL;
137 :
138 0 : m_poRelation = NULL;
139 0 : m_bRelFieldsCreated = FALSE;
140 0 : }
141 :
142 : /**********************************************************************
143 : * TABView::~TABView()
144 : *
145 : * Destructor.
146 : **********************************************************************/
147 0 : TABView::~TABView()
148 : {
149 0 : Close();
150 0 : }
151 :
152 :
153 0 : int TABView::GetFeatureCount (int bForce)
154 : {
155 :
156 0 : if (m_nMainTableIndex != -1)
157 0 : return m_papoTABFiles[m_nMainTableIndex]->GetFeatureCount( bForce );
158 :
159 0 : return 0;
160 : }
161 :
162 0 : void TABView::ResetReading()
163 : {
164 0 : if (m_nMainTableIndex != -1)
165 0 : m_papoTABFiles[m_nMainTableIndex]->ResetReading();
166 0 : }
167 :
168 :
169 : /**********************************************************************
170 : * TABView::Open()
171 : *
172 : * Open a .TAB dataset and the associated files, and initialize the
173 : * structures to be ready to read features from it.
174 : *
175 : * This class is used to open .TAB files that define a view on
176 : * two other .TAB files. Regular .TAB datasets should be opened using
177 : * the TABFile class instead.
178 : *
179 : * Set bTestOpenNoError=TRUE to silently return -1 with no error message
180 : * if the file cannot be opened. This is intended to be used in the
181 : * context of a TestOpen() function. The default value is FALSE which
182 : * means that an error is reported if the file cannot be opened.
183 : *
184 : * Returns 0 on success, -1 on error.
185 : **********************************************************************/
186 0 : int TABView::Open(const char *pszFname, const char *pszAccess,
187 : GBool bTestOpenNoError /*= FALSE*/ )
188 : {
189 0 : char nStatus = 0;
190 :
191 0 : if (m_numTABFiles > 0)
192 : {
193 : CPLError(CE_Failure, CPLE_AssertionFailed,
194 0 : "Open() failed: object already contains an open file");
195 0 : return -1;
196 : }
197 :
198 : /*-----------------------------------------------------------------
199 : * Validate access mode and call the right open method
200 : *----------------------------------------------------------------*/
201 0 : if (EQUALN(pszAccess, "r", 1))
202 : {
203 0 : m_eAccessMode = TABRead;
204 0 : nStatus = (char)OpenForRead(pszFname, bTestOpenNoError);
205 : }
206 0 : else if (EQUALN(pszAccess, "w", 1))
207 : {
208 0 : m_eAccessMode = TABWrite;
209 0 : nStatus = (char)OpenForWrite(pszFname);
210 : }
211 : else
212 : {
213 : CPLError(CE_Failure, CPLE_NotSupported,
214 0 : "Open() failed: access mode \"%s\" not supported", pszAccess);
215 0 : return -1;
216 : }
217 :
218 0 : return nStatus;
219 : }
220 :
221 :
222 : /**********************************************************************
223 : * TABView::OpenForRead()
224 : *
225 : * Open for reading
226 : *
227 : * Returns 0 on success, -1 on error.
228 : **********************************************************************/
229 0 : int TABView::OpenForRead(const char *pszFname,
230 : GBool bTestOpenNoError /*= FALSE*/ )
231 : {
232 0 : char *pszPath = NULL;
233 0 : int nFnameLen = 0;
234 :
235 0 : m_eAccessMode = TABRead;
236 :
237 : /*-----------------------------------------------------------------
238 : * Read main .TAB (text) file
239 : *----------------------------------------------------------------*/
240 0 : m_pszFname = CPLStrdup(pszFname);
241 :
242 : #ifndef _WIN32
243 : /*-----------------------------------------------------------------
244 : * On Unix, make sure extension uses the right cases
245 : * We do it even for write access because if a file with the same
246 : * extension already exists we want to overwrite it.
247 : *----------------------------------------------------------------*/
248 0 : TABAdjustFilenameExtension(m_pszFname);
249 : #endif
250 :
251 : /*-----------------------------------------------------------------
252 : * Open .TAB file... since it's a small text file, we will just load
253 : * it as a stringlist in memory.
254 : *----------------------------------------------------------------*/
255 0 : m_papszTABFile = TAB_CSLLoad(m_pszFname);
256 0 : if (m_papszTABFile == NULL)
257 : {
258 0 : if (!bTestOpenNoError)
259 : {
260 : CPLError(CE_Failure, CPLE_FileIO,
261 0 : "Failed opening %s.", m_pszFname);
262 : }
263 :
264 0 : CPLFree(m_pszFname);
265 0 : return -1;
266 : }
267 :
268 : /*-------------------------------------------------------------
269 : * Look for a line with the "create view" keyword.
270 : * If there is no "create view", then we may have a valid .TAB file,
271 : * but we do not support it in this class.
272 : *------------------------------------------------------------*/
273 0 : GBool bCreateViewFound = FALSE;
274 0 : for (int i=0;
275 0 : !bCreateViewFound && m_papszTABFile && m_papszTABFile[i];
276 : i++)
277 : {
278 0 : const char *pszStr = m_papszTABFile[i];
279 0 : while(*pszStr != '\0' && isspace((unsigned char)*pszStr))
280 0 : pszStr++;
281 0 : if (EQUALN(pszStr, "create view", 11))
282 0 : bCreateViewFound = TRUE;
283 : }
284 :
285 0 : if ( !bCreateViewFound )
286 : {
287 0 : if (!bTestOpenNoError)
288 : CPLError(CE_Failure, CPLE_NotSupported,
289 : "%s contains no table view definition. "
290 : "This type of .TAB file cannot be read by this library.",
291 0 : m_pszFname);
292 : else
293 0 : CPLErrorReset();
294 :
295 0 : CPLFree(m_pszFname);
296 :
297 0 : return -1;
298 : }
299 :
300 : /*-----------------------------------------------------------------
301 : * OK, this appears to be a valid TAB view dataset...
302 : * Extract the path component from the main .TAB filename
303 : * to build the filename of the sub-tables
304 : *----------------------------------------------------------------*/
305 0 : pszPath = CPLStrdup(m_pszFname);
306 0 : nFnameLen = strlen(pszPath);
307 0 : for( ; nFnameLen > 0; nFnameLen--)
308 : {
309 0 : if (pszPath[nFnameLen-1] == '/' ||
310 0 : pszPath[nFnameLen-1] == '\\' )
311 : {
312 0 : break;
313 : }
314 0 : pszPath[nFnameLen-1] = '\0';
315 : }
316 :
317 : /*-----------------------------------------------------------------
318 : * Extract the useful info from the TAB header
319 : *----------------------------------------------------------------*/
320 0 : if (ParseTABFile(pszPath, bTestOpenNoError) != 0)
321 : {
322 : // Failed parsing... an error has already been produced if necessary
323 0 : CPLFree(pszPath);
324 0 : Close();
325 0 : return -1;
326 : }
327 0 : CPLFree(pszPath);
328 0 : pszPath = NULL;
329 :
330 : /*-----------------------------------------------------------------
331 : * __TODO__ For now, we support only 2 files linked through a single
332 : * field... so we'll do some validation first to make sure
333 : * that what we found in the header respects these limitations.
334 : *----------------------------------------------------------------*/
335 0 : if (m_numTABFiles != 2)
336 : {
337 0 : if (!bTestOpenNoError)
338 : CPLError(CE_Failure, CPLE_NotSupported,
339 : "Open Failed: Dataset %s defines a view on %d tables. "
340 : "This is not currently supported.",
341 0 : m_pszFname, m_numTABFiles);
342 0 : Close();
343 0 : return -1;
344 : }
345 :
346 : /*-----------------------------------------------------------------
347 : * Open all the tab files listed in the view
348 : *----------------------------------------------------------------*/
349 0 : m_papoTABFiles = (TABFile**)CPLCalloc(m_numTABFiles, sizeof(TABFile*));
350 :
351 0 : for (int iFile=0; iFile < m_numTABFiles; iFile++)
352 : {
353 : #ifndef _WIN32
354 0 : TABAdjustFilenameExtension(m_papszTABFnames[iFile]);
355 : #endif
356 :
357 0 : m_papoTABFiles[iFile] = new TABFile;
358 :
359 0 : if ( m_papoTABFiles[iFile]->Open(m_papszTABFnames[iFile],
360 0 : "rb", bTestOpenNoError) != 0)
361 : {
362 : // Open Failed... an error has already been reported, just return.
363 0 : if (bTestOpenNoError)
364 0 : CPLErrorReset();
365 0 : Close();
366 0 : return -1;
367 : }
368 : }
369 :
370 : /*-----------------------------------------------------------------
371 : * Create TABRelation... this will build FeatureDefn, etc.
372 : * __TODO__ For now this assumes only 2 tables in the view...
373 : *----------------------------------------------------------------*/
374 0 : m_poRelation = new TABRelation;
375 :
376 0 : CPLAssert(m_nMainTableIndex == 0);
377 0 : CPLAssert(CSLCount(m_papszWhereClause) == 5);
378 0 : char *pszTableName = TABGetBasename(m_pszFname);
379 0 : if ( m_poRelation->Init(pszTableName,
380 : m_papoTABFiles[0], m_papoTABFiles[1],
381 0 : m_papszWhereClause[4], m_papszWhereClause[2],
382 : m_papszFieldNames) != 0 )
383 : {
384 : // An error should already have been reported
385 0 : CPLFree(pszTableName);
386 0 : Close();
387 0 : return -1;
388 : }
389 0 : CPLFree(pszTableName);
390 :
391 0 : return 0;
392 : }
393 :
394 :
395 : /**********************************************************************
396 : * TABView::OpenForWrite()
397 : *
398 : * Create a new TABView dataset
399 : *
400 : * Returns 0 on success, -1 on error.
401 : **********************************************************************/
402 0 : int TABView::OpenForWrite(const char *pszFname)
403 : {
404 0 : int nFnameLen = 0;
405 :
406 0 : m_eAccessMode = TABWrite;
407 :
408 : /*-----------------------------------------------------------------
409 : * Read main .TAB (text) file
410 : *----------------------------------------------------------------*/
411 0 : m_pszFname = CPLStrdup(pszFname);
412 :
413 : #ifndef _WIN32
414 : /*-----------------------------------------------------------------
415 : * On Unix, make sure extension uses the right cases
416 : * We do it even for write access because if a file with the same
417 : * extension already exists we want to overwrite it.
418 : *----------------------------------------------------------------*/
419 0 : TABAdjustFilenameExtension(m_pszFname);
420 : #endif
421 :
422 : /*-----------------------------------------------------------------
423 : * Extract the path component from the main .TAB filename
424 : *----------------------------------------------------------------*/
425 0 : char *pszPath = CPLStrdup(m_pszFname);
426 0 : nFnameLen = strlen(pszPath);
427 0 : for( ; nFnameLen > 0; nFnameLen--)
428 : {
429 0 : if (pszPath[nFnameLen-1] == '/' ||
430 0 : pszPath[nFnameLen-1] == '\\' )
431 : {
432 0 : break;
433 : }
434 0 : pszPath[nFnameLen-1] = '\0';
435 : }
436 :
437 0 : char *pszBasename = TABGetBasename(m_pszFname);
438 :
439 : /*-----------------------------------------------------------------
440 : * Create the 2 TAB files for the view.
441 : *
442 : * __TODO__ For now, we support only 2 files linked through a single
443 : * field... not sure if anything else than that can be useful
444 : * anyways.
445 : *----------------------------------------------------------------*/
446 0 : m_numTABFiles = 2;
447 0 : m_papszTABFnames = NULL;
448 0 : m_nMainTableIndex = 0;
449 0 : m_bRelFieldsCreated = FALSE;
450 :
451 0 : m_papoTABFiles = (TABFile**)CPLCalloc(m_numTABFiles, sizeof(TABFile*));
452 :
453 0 : for (int iFile=0; iFile < m_numTABFiles; iFile++)
454 : {
455 : m_papszTABFnames = CSLAppendPrintf(m_papszTABFnames, "%s%s%d.tab",
456 0 : pszPath, pszBasename, iFile+1);
457 : #ifndef _WIN32
458 0 : TABAdjustFilenameExtension(m_papszTABFnames[iFile]);
459 : #endif
460 :
461 0 : m_papoTABFiles[iFile] = new TABFile;
462 :
463 0 : if ( m_papoTABFiles[iFile]->Open(m_papszTABFnames[iFile], "wb") != 0)
464 : {
465 : // Open Failed... an error has already been reported, just return.
466 0 : CPLFree(pszPath);
467 0 : CPLFree(pszBasename);
468 0 : Close();
469 0 : return -1;
470 : }
471 : }
472 :
473 : /*-----------------------------------------------------------------
474 : * Create TABRelation...
475 : *----------------------------------------------------------------*/
476 0 : m_poRelation = new TABRelation;
477 :
478 0 : if ( m_poRelation->Init(pszBasename,
479 : m_papoTABFiles[0], m_papoTABFiles[1],
480 : NULL, NULL, NULL) != 0 )
481 : {
482 : // An error should already have been reported
483 0 : CPLFree(pszPath);
484 0 : CPLFree(pszBasename);
485 0 : Close();
486 0 : return -1;
487 : }
488 :
489 0 : CPLFree(pszPath);
490 0 : CPLFree(pszBasename);
491 :
492 0 : return 0;
493 : }
494 :
495 :
496 :
497 : /**********************************************************************
498 : * TABView::ParseTABFile()
499 : *
500 : * Scan the lines of the TAB file, and store any useful information into
501 : * class members. The main piece of information being the sub-table
502 : * names, and the list of fields to include in the view that we will
503 : * use to build the OGRFeatureDefn for this file.
504 : *
505 : * It is assumed that the TAB header file is already loaded in m_papszTABFile
506 : *
507 : * This private method should be used only during the Open() call.
508 : *
509 : * Returns 0 on success, -1 on error.
510 : **********************************************************************/
511 0 : int TABView::ParseTABFile(const char *pszDatasetPath,
512 : GBool bTestOpenNoError /*=FALSE*/)
513 : {
514 : int iLine, numLines;
515 0 : char **papszTok=NULL;
516 0 : GBool bInsideTableDef = FALSE;
517 :
518 0 : if (m_eAccessMode != TABRead)
519 : {
520 : CPLError(CE_Failure, CPLE_AssertionFailed,
521 0 : "ParseTABFile() can be used only with Read access.");
522 0 : return -1;
523 : }
524 :
525 0 : numLines = CSLCount(m_papszTABFile);
526 :
527 0 : for(iLine=0; iLine<numLines; iLine++)
528 : {
529 : /*-------------------------------------------------------------
530 : * Tokenize the next .TAB line, and check first keyword
531 : *------------------------------------------------------------*/
532 0 : CSLDestroy(papszTok);
533 0 : papszTok = CSLTokenizeStringComplex(m_papszTABFile[iLine], " \t(),;",
534 0 : TRUE, FALSE);
535 0 : if (CSLCount(papszTok) < 2)
536 0 : continue; // All interesting lines have at least 2 tokens
537 :
538 0 : if (EQUAL(papszTok[0], "!version"))
539 : {
540 0 : m_pszVersion = CPLStrdup(papszTok[1]);
541 : }
542 0 : else if (EQUAL(papszTok[0], "!charset"))
543 : {
544 0 : CPLFree(m_pszCharset);
545 0 : m_pszCharset = CPLStrdup(papszTok[1]);
546 : }
547 0 : else if (EQUAL(papszTok[0], "open") &&
548 0 : EQUAL(papszTok[1], "table") &&
549 : CSLCount(papszTok) >= 3)
550 : {
551 : // Source table name may be either "filename" or "filename.tab"
552 0 : int nLen = strlen(papszTok[2]);
553 0 : if (nLen > 4 && EQUAL(papszTok[2]+nLen-4, ".tab"))
554 0 : papszTok[2][nLen-4] = '\0';
555 :
556 : m_papszTABFnames = CSLAppendPrintf(m_papszTABFnames,
557 : "%s%s.tab",
558 0 : pszDatasetPath, papszTok[2]);
559 : }
560 0 : else if (EQUAL(papszTok[0], "create") &&
561 0 : EQUAL(papszTok[1], "view") )
562 : {
563 0 : bInsideTableDef = TRUE;
564 : }
565 0 : else if (bInsideTableDef &&
566 0 : (EQUAL(papszTok[0],"Select")))
567 : {
568 : /*---------------------------------------------------------
569 : * We found the list of table fields (comma-delimited list)
570 : *--------------------------------------------------------*/
571 : int iTok;
572 0 : for(iTok=1; papszTok[iTok] != NULL; iTok++)
573 : m_papszFieldNames = CSLAddString(m_papszFieldNames,
574 0 : papszTok[iTok]);
575 :
576 : }
577 0 : else if (bInsideTableDef &&
578 0 : (EQUAL(papszTok[0],"where")))
579 : {
580 : /*---------------------------------------------------------
581 : * We found the where clause that relates the 2 tables
582 : * Something in the form:
583 : * where table1.field1=table2.field2
584 : * The tokenized array will contain:
585 : * {"where", "table1", "field1", "table2", "field2"}
586 : *--------------------------------------------------------*/
587 0 : m_papszWhereClause =CSLTokenizeStringComplex(m_papszTABFile[iLine],
588 : " \t(),;=.",
589 0 : TRUE, FALSE);
590 :
591 : /*---------------------------------------------------------
592 : * For now we are very limiting on the format of the WHERE
593 : * clause... we will be more permitting as we learn more about
594 : * what it can contain... (I don't want to implement a full SQL
595 : * parser here!!!). If you encountered this error,
596 : * (and are reading this!) please report the test dataset
597 : * that produced the error and I'll see if we can support it.
598 : *--------------------------------------------------------*/
599 0 : if (CSLCount( m_papszWhereClause ) != 5)
600 : {
601 0 : if (!bTestOpenNoError)
602 : CPLError(CE_Failure, CPLE_NotSupported,
603 : "WHERE clause in %s is not in a supported format: \"%s\"",
604 0 : m_pszFname, m_papszTABFile[iLine]);
605 0 : return -1;
606 : }
607 : }
608 : else
609 : {
610 : // Simply Ignore unrecognized lines
611 : }
612 : }
613 :
614 0 : CSLDestroy(papszTok);
615 :
616 : /*-----------------------------------------------------------------
617 : * The main table is the one from which we read the geometries, etc...
618 : * For now we assume it is always the first one in the list
619 : *----------------------------------------------------------------*/
620 0 : m_nMainTableIndex = 0;
621 :
622 : /*-----------------------------------------------------------------
623 : * Make sure all required class members are set
624 : *----------------------------------------------------------------*/
625 0 : m_numTABFiles = CSLCount(m_papszTABFnames);
626 :
627 0 : if (m_pszCharset == NULL)
628 0 : m_pszCharset = CPLStrdup("Neutral");
629 0 : if (m_pszVersion == NULL)
630 0 : m_pszVersion = CPLStrdup("100");
631 :
632 0 : if (CSLCount(m_papszFieldNames) == 0 )
633 : {
634 0 : if (!bTestOpenNoError)
635 : CPLError(CE_Failure, CPLE_NotSupported,
636 : "%s: header contains no table field definition. "
637 : "This type of .TAB file cannot be read by this library.",
638 0 : m_pszFname);
639 0 : return -1;
640 : }
641 :
642 0 : if (CSLCount(m_papszWhereClause) == 0 )
643 : {
644 0 : if (!bTestOpenNoError)
645 : CPLError(CE_Failure, CPLE_NotSupported,
646 : "%s: WHERE clause not found or missing in header. "
647 : "This type of .TAB file cannot be read by this library.",
648 0 : m_pszFname);
649 0 : return -1;
650 : }
651 0 : return 0;
652 : }
653 :
654 :
655 : /**********************************************************************
656 : * TABView::WriteTABFile()
657 : *
658 : * Generate the TAB header file. This is usually done during the
659 : * Close() call.
660 : *
661 : * Returns 0 on success, -1 on error.
662 : **********************************************************************/
663 0 : int TABView::WriteTABFile()
664 : {
665 : FILE *fp;
666 :
667 0 : CPLAssert(m_eAccessMode == TABWrite);
668 0 : CPLAssert(m_numTABFiles == 2);
669 0 : CPLAssert(GetLayerDefn());
670 :
671 0 : char *pszTable = TABGetBasename(m_pszFname);
672 0 : char *pszTable1 = TABGetBasename(m_papszTABFnames[0]);
673 0 : char *pszTable2 = TABGetBasename(m_papszTABFnames[1]);
674 :
675 0 : if ( (fp = VSIFOpen(m_pszFname, "wt")) != NULL)
676 : {
677 : // Version is always 100, no matter what the sub-table's version is
678 0 : fprintf(fp, "!Table\n");
679 0 : fprintf(fp, "!Version 100\n");
680 :
681 0 : fprintf(fp, "Open Table \"%s\" Hide\n", pszTable1);
682 0 : fprintf(fp, "Open Table \"%s\" Hide\n", pszTable2);
683 0 : fprintf(fp, "\n");
684 0 : fprintf(fp, "Create View %s As\n", pszTable);
685 0 : fprintf(fp, "Select ");
686 :
687 0 : OGRFeatureDefn *poDefn = GetLayerDefn();
688 0 : for(int iField=0; iField<poDefn->GetFieldCount(); iField++)
689 : {
690 0 : OGRFieldDefn *poFieldDefn = poDefn->GetFieldDefn(iField);
691 0 : if (iField == 0)
692 0 : fprintf(fp, "%s", poFieldDefn->GetNameRef());
693 : else
694 0 : fprintf(fp, ",%s", poFieldDefn->GetNameRef());
695 : }
696 0 : fprintf(fp, "\n");
697 :
698 0 : fprintf(fp, "From %s, %s\n", pszTable2, pszTable1);
699 : fprintf(fp, "Where %s.%s=%s.%s\n", pszTable2,
700 : m_poRelation->GetRelFieldName(),
701 : pszTable1,
702 0 : m_poRelation->GetMainFieldName());
703 :
704 :
705 0 : VSIFClose(fp);
706 : }
707 : else
708 : {
709 0 : CPLFree(pszTable);
710 0 : CPLFree(pszTable1);
711 0 : CPLFree(pszTable2);
712 :
713 : CPLError(CE_Failure, CPLE_FileIO,
714 0 : "Failed to create file `%s'", m_pszFname);
715 0 : return -1;
716 : }
717 :
718 0 : CPLFree(pszTable);
719 0 : CPLFree(pszTable1);
720 0 : CPLFree(pszTable2);
721 :
722 0 : return 0;
723 : }
724 :
725 :
726 : /**********************************************************************
727 : * TABView::Close()
728 : *
729 : * Close current file, and release all memory used.
730 : *
731 : * Returns 0 on success, -1 on error.
732 : **********************************************************************/
733 0 : int TABView::Close()
734 : {
735 : // In write access, the main .TAB file has not been written yet.
736 0 : if (m_eAccessMode == TABWrite && m_poRelation)
737 0 : WriteTABFile();
738 :
739 0 : for(int i=0; m_papoTABFiles && i<m_numTABFiles; i++)
740 : {
741 0 : if (m_papoTABFiles[i])
742 0 : delete m_papoTABFiles[i]; // Automatically closes.
743 : }
744 0 : CPLFree(m_papoTABFiles);
745 0 : m_papoTABFiles = NULL;
746 0 : m_numTABFiles = 0;
747 :
748 :
749 : /*-----------------------------------------------------------------
750 : * __TODO__ OK, MapInfo does not like to see a .map and .id file
751 : * attached to the second table, even if they're empty.
752 : * We'll use a little hack to delete them now, but eventually we
753 : * should avoid creating them at all.
754 : *----------------------------------------------------------------*/
755 0 : if (m_eAccessMode == TABWrite && m_pszFname)
756 : {
757 0 : m_pszFname[strlen(m_pszFname)-4] = '\0';
758 0 : char *pszFile = CPLStrdup(CPLSPrintf("%s2.map", m_pszFname));
759 0 : TABAdjustFilenameExtension(pszFile);
760 0 : VSIUnlink(pszFile);
761 :
762 0 : sprintf(pszFile, "%s2.id", m_pszFname);
763 0 : TABAdjustFilenameExtension(pszFile);
764 0 : VSIUnlink(pszFile);
765 :
766 0 : CPLFree(pszFile);
767 : }
768 : // End of hack!
769 :
770 :
771 0 : CPLFree(m_pszFname);
772 0 : m_pszFname = NULL;
773 :
774 0 : CSLDestroy(m_papszTABFile);
775 0 : m_papszTABFile = NULL;
776 :
777 0 : CPLFree(m_pszVersion);
778 0 : m_pszVersion = NULL;
779 0 : CPLFree(m_pszCharset);
780 0 : m_pszCharset = NULL;
781 :
782 0 : CSLDestroy(m_papszTABFnames);
783 0 : m_papszTABFnames = NULL;
784 :
785 0 : CSLDestroy(m_papszFieldNames);
786 0 : m_papszFieldNames = NULL;
787 0 : CSLDestroy(m_papszWhereClause);
788 0 : m_papszWhereClause = NULL;
789 :
790 0 : m_nMainTableIndex = -1;
791 :
792 0 : if (m_poRelation)
793 0 : delete m_poRelation;
794 0 : m_poRelation = NULL;
795 :
796 0 : m_bRelFieldsCreated = FALSE;
797 :
798 0 : return 0;
799 : }
800 :
801 : /**********************************************************************
802 : * TABView::SetQuickSpatialIndexMode()
803 : *
804 : * Select "quick spatial index mode".
805 : *
806 : * The default behavior of MITAB is to generate an optimized spatial index,
807 : * but this results in slower write speed.
808 : *
809 : * Applications that want faster write speed and do not care
810 : * about the performance of spatial queries on the resulting file can
811 : * use SetQuickSpatialIndexMode() to require the creation of a non-optimal
812 : * spatial index (actually emulating the type of spatial index produced
813 : * by MITAB before version 1.6.0). In this mode writing files can be
814 : * about 5 times faster, but spatial queries can be up to 30 times slower.
815 : *
816 : * Returns 0 on success, -1 on error.
817 : **********************************************************************/
818 0 : int TABView::SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode/*=TRUE*/)
819 : {
820 0 : if (m_eAccessMode != TABWrite || m_numTABFiles == 0)
821 : {
822 : CPLError(CE_Failure, CPLE_AssertionFailed,
823 0 : "SetQuickSpatialIndexMode() failed: file not opened for write access.");
824 0 : return -1;
825 : }
826 :
827 0 : for (int iFile=0; iFile < m_numTABFiles; iFile++)
828 : {
829 0 : if ( m_papoTABFiles[iFile]->SetQuickSpatialIndexMode(bQuickSpatialIndexMode) != 0)
830 : {
831 : // An error has already been reported, just return.
832 0 : return -1;
833 : }
834 : }
835 :
836 0 : return 0;
837 : }
838 :
839 :
840 : /**********************************************************************
841 : * TABView::GetNextFeatureId()
842 : *
843 : * Returns feature id that follows nPrevId, or -1 if it is the
844 : * last feature id. Pass nPrevId=-1 to fetch the first valid feature id.
845 : **********************************************************************/
846 0 : int TABView::GetNextFeatureId(int nPrevId)
847 : {
848 0 : if (m_nMainTableIndex != -1)
849 0 : return m_papoTABFiles[m_nMainTableIndex]->GetNextFeatureId(nPrevId);
850 :
851 0 : return -1;
852 : }
853 :
854 : /**********************************************************************
855 : * TABView::GetFeatureRef()
856 : *
857 : * Fill and return a TABFeature object for the specified feature id.
858 : *
859 : * The retruned pointer is a reference to an object owned and maintained
860 : * by this TABView object. It should not be altered or freed by the
861 : * caller and its contents is guaranteed to be valid only until the next
862 : * call to GetFeatureRef() or Close().
863 : *
864 : * Returns NULL if the specified feature id does not exist of if an
865 : * error happened. In any case, CPLError() will have been called to
866 : * report the reason of the failure.
867 : **********************************************************************/
868 0 : TABFeature *TABView::GetFeatureRef(int nFeatureId)
869 : {
870 :
871 : /*-----------------------------------------------------------------
872 : * Make sure file is opened
873 : *----------------------------------------------------------------*/
874 0 : if (m_poRelation == NULL)
875 : {
876 : CPLError(CE_Failure, CPLE_IllegalArg,
877 0 : "GetFeatureRef() failed: file is not opened!");
878 0 : return NULL;
879 : }
880 :
881 0 : if(m_poCurFeature)
882 : {
883 0 : delete m_poCurFeature;
884 0 : m_poCurFeature = NULL;
885 : }
886 :
887 0 : m_poCurFeature = m_poRelation->GetFeature(nFeatureId);
888 0 : m_nCurFeatureId = nFeatureId;
889 0 : m_poCurFeature->SetFID(m_nCurFeatureId);
890 0 : return m_poCurFeature;
891 : }
892 :
893 :
894 : /**********************************************************************
895 : * TABView::CreateFeature()
896 : *
897 : * Write a new feature to this dataset. The passed in feature is updated
898 : * with the new feature id.
899 : *
900 : * Returns OGRERR_NONE on success, or an appropriate OGRERR_ code if an
901 : * error happened in which case, CPLError() will have been called to
902 : * report the reason of the failure.
903 : **********************************************************************/
904 0 : OGRErr TABView::CreateFeature(TABFeature *poFeature)
905 : {
906 0 : if (m_eAccessMode != TABWrite)
907 : {
908 : CPLError(CE_Failure, CPLE_NotSupported,
909 0 : "CreateFeature() can be used only with Write access.");
910 0 : return OGRERR_UNSUPPORTED_OPERATION;
911 : }
912 :
913 0 : if (m_poRelation == NULL)
914 : {
915 : CPLError(CE_Failure, CPLE_IllegalArg,
916 0 : "CreateFeature() failed: file is not opened!");
917 0 : return OGRERR_FAILURE;
918 : }
919 :
920 : /*-----------------------------------------------------------------
921 : * If we're about to write the first feature, then we must finish
922 : * the initialization of the view first by creating the MI_refnum fields
923 : *----------------------------------------------------------------*/
924 0 : if (!m_bRelFieldsCreated)
925 : {
926 0 : if (m_poRelation->CreateRelFields() != 0)
927 0 : return OGRERR_FAILURE;
928 0 : m_bRelFieldsCreated = TRUE;
929 : }
930 :
931 0 : int nFeatureId = m_poRelation->WriteFeature(poFeature);
932 0 : if (nFeatureId < 0)
933 0 : return OGRERR_FAILURE;
934 :
935 0 : poFeature->SetFID(nFeatureId);
936 :
937 0 : return OGRERR_NONE;
938 : }
939 :
940 :
941 :
942 : /**********************************************************************
943 : * TABView::GetLayerDefn()
944 : *
945 : * Returns a reference to the OGRFeatureDefn that will be used to create
946 : * features in this dataset.
947 : *
948 : * Returns a reference to an object that is maintained by this TABView
949 : * object (and thus should not be modified or freed by the caller) or
950 : * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file
951 : * opened yet)
952 : **********************************************************************/
953 0 : OGRFeatureDefn *TABView::GetLayerDefn()
954 : {
955 0 : if (m_poRelation)
956 0 : return m_poRelation->GetFeatureDefn();
957 :
958 0 : return NULL;
959 : }
960 :
961 : /**********************************************************************
962 : * TABView::SetFeatureDefn()
963 : *
964 : * Set the FeatureDefn for this dataset.
965 : *
966 : * For now, fields passed through SetFeatureDefn will not be mapped
967 : * properly, so this function can be used only with an empty feature defn.
968 : **********************************************************************/
969 0 : int TABView::SetFeatureDefn(OGRFeatureDefn *poFeatureDefn,
970 : TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */)
971 : {
972 0 : if (m_poRelation)
973 0 : return m_poRelation->SetFeatureDefn(poFeatureDefn);
974 :
975 0 : return -1;
976 : }
977 :
978 :
979 : /**********************************************************************
980 : * TABView::GetNativeFieldType()
981 : *
982 : * Returns the native MapInfo field type for the specified field.
983 : *
984 : * Returns TABFUnknown if file is not opened, or if specified field index is
985 : * invalid.
986 : *
987 : * Note that field ids are positive and start at 0.
988 : **********************************************************************/
989 0 : TABFieldType TABView::GetNativeFieldType(int nFieldId)
990 : {
991 0 : if (m_poRelation)
992 0 : return m_poRelation->GetNativeFieldType(nFieldId);
993 :
994 0 : return TABFUnknown;
995 : }
996 :
997 :
998 : /**********************************************************************
999 : * TABView::AddFieldNative()
1000 : *
1001 : * Create a new field using a native mapinfo data type... this is an
1002 : * alternative to defining fields through the OGR interface.
1003 : * This function should be called after creating a new dataset, but before
1004 : * writing the first feature.
1005 : *
1006 : * This function will build/update the OGRFeatureDefn that will have to be
1007 : * used when writing features to this dataset.
1008 : *
1009 : * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn().
1010 : *
1011 : * Returns 0 on success, -1 on error.
1012 : **********************************************************************/
1013 0 : int TABView::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
1014 : int nWidth /*=0*/, int nPrecision /*=0*/,
1015 : GBool bIndexed /*=FALSE*/, GBool bUnique/*=FALSE*/, int bApproxOK)
1016 : {
1017 0 : if (m_poRelation)
1018 : return m_poRelation->AddFieldNative(pszName, eMapInfoType,
1019 : nWidth, nPrecision,
1020 0 : bIndexed, bUnique, bApproxOK);
1021 :
1022 0 : return -1;
1023 : }
1024 :
1025 : /**********************************************************************
1026 : * TABView::SetFieldIndexed()
1027 : *
1028 : * Request that a field be indexed. This will create the .IND file if
1029 : * necessary, etc.
1030 : *
1031 : * Note that field ids are positive and start at 0.
1032 : *
1033 : * Returns 0 on success, -1 on error.
1034 : **********************************************************************/
1035 0 : int TABView::SetFieldIndexed(int nFieldId)
1036 : {
1037 0 : if (m_poRelation)
1038 0 : return m_poRelation->SetFieldIndexed(nFieldId);
1039 :
1040 0 : return -1;
1041 : }
1042 :
1043 : /**********************************************************************
1044 : * TABView::IsFieldIndexed()
1045 : *
1046 : * Returns TRUE if field is indexed, or FALSE otherwise.
1047 : **********************************************************************/
1048 0 : GBool TABView::IsFieldIndexed(int nFieldId)
1049 : {
1050 0 : if (m_poRelation)
1051 0 : return m_poRelation->IsFieldIndexed(nFieldId);
1052 :
1053 0 : return FALSE;
1054 : }
1055 :
1056 : /**********************************************************************
1057 : * TABView::IsFieldUnique()
1058 : *
1059 : * Returns TRUE if field is in the Unique table, or FALSE otherwise.
1060 : **********************************************************************/
1061 0 : GBool TABView::IsFieldUnique(int nFieldId)
1062 : {
1063 0 : if (m_poRelation)
1064 0 : return m_poRelation->IsFieldUnique(nFieldId);
1065 :
1066 0 : return FALSE;
1067 : }
1068 :
1069 :
1070 : /**********************************************************************
1071 : * TABView::GetBounds()
1072 : *
1073 : * Fetch projection coordinates bounds of a dataset.
1074 : *
1075 : * The bForce flag has no effect on TAB files since the bounds are
1076 : * always in the header.
1077 : *
1078 : * Returns 0 on success, -1 on error.
1079 : **********************************************************************/
1080 0 : int TABView::GetBounds(double &dXMin, double &dYMin,
1081 : double &dXMax, double &dYMax,
1082 : GBool bForce /*= TRUE*/)
1083 : {
1084 0 : if (m_nMainTableIndex == -1)
1085 : {
1086 : CPLError(CE_Failure, CPLE_AppDefined,
1087 0 : "GetBounds() can be called only after dataset has been opened.");
1088 0 : return -1;
1089 : }
1090 :
1091 0 : return m_papoTABFiles[m_nMainTableIndex]->GetBounds(dXMin, dYMin,
1092 : dXMax, dYMax,
1093 0 : bForce);
1094 : }
1095 :
1096 : /**********************************************************************
1097 : * TABView::GetExtent()
1098 : *
1099 : * Fetch extent of the data currently stored in the dataset.
1100 : *
1101 : * The bForce flag has no effect on TAB files since that value is
1102 : * always in the header.
1103 : *
1104 : * Returns OGRERR_NONE/OGRRERR_FAILURE.
1105 : **********************************************************************/
1106 0 : OGRErr TABView::GetExtent (OGREnvelope *psExtent, int bForce)
1107 : {
1108 0 : if (m_nMainTableIndex == -1)
1109 : {
1110 : CPLError(CE_Failure, CPLE_AppDefined,
1111 0 : "GetExtent() can be called only after dataset has been opened.");
1112 0 : return -1;
1113 : }
1114 :
1115 0 : return m_papoTABFiles[m_nMainTableIndex]->GetExtent(psExtent, bForce);
1116 :
1117 : }
1118 :
1119 : /**********************************************************************
1120 : * TABView::GetFeatureCountByType()
1121 : *
1122 : * Return number of features of each type.
1123 : *
1124 : * Note that the sum of the 4 returned values may be different from
1125 : * the total number of features since features with NONE geometry
1126 : * are not taken into account here.
1127 : *
1128 : * Note: the bForce flag has nmo effect on .TAB files since the info
1129 : * is always in the header.
1130 : *
1131 : * Returns 0 on success, or silently returns -1 (with no error) if this
1132 : * information is not available.
1133 : **********************************************************************/
1134 0 : int TABView::GetFeatureCountByType(int &numPoints, int &numLines,
1135 : int &numRegions, int &numTexts,
1136 : GBool bForce /*= TRUE*/)
1137 : {
1138 0 : if (m_nMainTableIndex == -1)
1139 0 : return -1;
1140 :
1141 0 : return m_papoTABFiles[m_nMainTableIndex]->GetFeatureCountByType(numPoints,
1142 : numLines,
1143 : numRegions,
1144 : numTexts,
1145 0 : bForce);
1146 : }
1147 :
1148 :
1149 : /**********************************************************************
1150 : * TABView::GetSpatialRef()
1151 : *
1152 : * Returns a reference to an OGRSpatialReference for this dataset.
1153 : * If the projection parameters have not been parsed yet, then we will
1154 : * parse them before returning.
1155 : *
1156 : * The returned object is owned and maintained by this TABFile and
1157 : * should not be modified or freed by the caller.
1158 : *
1159 : * Returns NULL if the SpatialRef cannot be accessed.
1160 : **********************************************************************/
1161 0 : OGRSpatialReference *TABView::GetSpatialRef()
1162 : {
1163 0 : if (m_nMainTableIndex == -1)
1164 : {
1165 : CPLError(CE_Failure, CPLE_AssertionFailed,
1166 0 : "GetSpatialRef() failed: file has not been opened yet.");
1167 0 : return NULL;
1168 : }
1169 :
1170 0 : return m_papoTABFiles[m_nMainTableIndex]->GetSpatialRef();
1171 : }
1172 :
1173 : /**********************************************************************
1174 : * TABView::SetSpatialRef()
1175 : **********************************************************************/
1176 0 : int TABView::SetSpatialRef(OGRSpatialReference *poSpatialRef)
1177 : {
1178 0 : if (m_nMainTableIndex == -1)
1179 : {
1180 : CPLError(CE_Failure, CPLE_AssertionFailed,
1181 0 : "SetSpatialRef() failed: file has not been opened yet.");
1182 0 : return -1;
1183 : }
1184 :
1185 0 : return m_papoTABFiles[m_nMainTableIndex]->SetSpatialRef(poSpatialRef);
1186 : }
1187 :
1188 :
1189 :
1190 : /**********************************************************************
1191 : * TABView::SetBounds()
1192 : **********************************************************************/
1193 0 : int TABView::SetBounds(double dXMin, double dYMin,
1194 : double dXMax, double dYMax)
1195 : {
1196 0 : if (m_nMainTableIndex == -1)
1197 : {
1198 : CPLError(CE_Failure, CPLE_AssertionFailed,
1199 0 : "SetBounds() failed: file has not been opened yet.");
1200 0 : return -1;
1201 : }
1202 :
1203 0 : return m_papoTABFiles[m_nMainTableIndex]->SetBounds(dXMin, dYMin,
1204 0 : dXMax, dYMax);
1205 : }
1206 :
1207 : /************************************************************************/
1208 : /* TestCapability() */
1209 : /************************************************************************/
1210 :
1211 0 : int TABView::TestCapability( const char * pszCap )
1212 :
1213 : {
1214 0 : if( EQUAL(pszCap,OLCRandomRead) )
1215 0 : return TRUE;
1216 :
1217 0 : else if( EQUAL(pszCap,OLCSequentialWrite))
1218 0 : return TRUE;
1219 :
1220 0 : else if( EQUAL(pszCap,OLCRandomWrite))
1221 0 : return FALSE;
1222 :
1223 0 : else if( EQUAL(pszCap,OLCFastFeatureCount) )
1224 0 : return m_poFilterGeom == NULL;
1225 :
1226 0 : else if( EQUAL(pszCap,OLCFastSpatialFilter) )
1227 0 : return FALSE;
1228 :
1229 0 : else if( EQUAL(pszCap,OLCFastGetExtent) )
1230 0 : return TRUE;
1231 :
1232 : else
1233 0 : return FALSE;
1234 : }
1235 :
1236 :
1237 :
1238 :
1239 :
1240 :
1241 : /**********************************************************************
1242 : * TABView::Dump()
1243 : *
1244 : * Dump block contents... available only in DEBUG mode.
1245 : **********************************************************************/
1246 : #ifdef DEBUG
1247 :
1248 0 : void TABView::Dump(FILE *fpOut /*=NULL*/)
1249 : {
1250 0 : if (fpOut == NULL)
1251 0 : fpOut = stdout;
1252 :
1253 0 : fprintf(fpOut, "----- TABView::Dump() -----\n");
1254 :
1255 0 : if (m_numTABFiles > 0)
1256 : {
1257 0 : fprintf(fpOut, "File is not opened.\n");
1258 : }
1259 : else
1260 : {
1261 0 : fprintf(fpOut, "File is opened: %s\n", m_pszFname);
1262 0 : fprintf(fpOut, "View contains %d tables\n", m_numTABFiles);
1263 :
1264 : }
1265 :
1266 0 : fflush(fpOut);
1267 0 : }
1268 :
1269 : #endif // DEBUG
1270 :
1271 :
1272 :
1273 : /*=====================================================================
1274 : * class TABRelation
1275 : *====================================================================*/
1276 :
1277 :
1278 : /**********************************************************************
1279 : * TABRelation::TABRelation()
1280 : *
1281 : * Constructor.
1282 : **********************************************************************/
1283 0 : TABRelation::TABRelation()
1284 : {
1285 0 : m_poMainTable = NULL;
1286 0 : m_pszMainFieldName = NULL;
1287 0 : m_nMainFieldNo = -1;
1288 :
1289 0 : m_poRelTable = NULL;
1290 0 : m_pszRelFieldName = NULL;
1291 0 : m_nRelFieldNo = -1;
1292 0 : m_nRelFieldIndexNo = -1;
1293 0 : m_poRelINDFileRef = NULL;
1294 :
1295 0 : m_nUniqueRecordNo = 0;
1296 :
1297 0 : m_panMainTableFieldMap = NULL;
1298 0 : m_panRelTableFieldMap = NULL;
1299 :
1300 0 : m_poDefn = NULL;
1301 0 : }
1302 :
1303 : /**********************************************************************
1304 : * TABRelation::~TABRelation()
1305 : *
1306 : * Destructor.
1307 : **********************************************************************/
1308 0 : TABRelation::~TABRelation()
1309 : {
1310 0 : ResetAllMembers();
1311 0 : }
1312 :
1313 : /**********************************************************************
1314 : * TABRelation::ResetAllMembers()
1315 : *
1316 : * Reset all class members.
1317 : **********************************************************************/
1318 0 : void TABRelation::ResetAllMembers()
1319 : {
1320 0 : m_poMainTable = NULL;
1321 0 : CPLFree(m_pszMainFieldName);
1322 0 : m_pszMainFieldName = NULL;
1323 0 : m_nMainFieldNo = -1;
1324 :
1325 0 : m_poRelTable = NULL;
1326 0 : CPLFree(m_pszRelFieldName);
1327 0 : m_pszRelFieldName = NULL;
1328 0 : m_nRelFieldNo = -1;
1329 0 : m_nRelFieldIndexNo = -1;
1330 :
1331 0 : m_nUniqueRecordNo = 0;
1332 :
1333 : // No need to close m_poRelINDFileRef since we only got a ref. to it
1334 0 : m_poRelINDFileRef = NULL;
1335 :
1336 0 : CPLFree(m_panMainTableFieldMap);
1337 0 : m_panMainTableFieldMap = NULL;
1338 0 : CPLFree(m_panRelTableFieldMap);
1339 0 : m_panRelTableFieldMap = NULL;
1340 :
1341 : /*-----------------------------------------------------------------
1342 : * Note: we have to check the reference count before deleting m_poDefn
1343 : *----------------------------------------------------------------*/
1344 0 : if (m_poDefn && m_poDefn->Dereference() == 0)
1345 0 : delete m_poDefn;
1346 0 : m_poDefn = NULL;
1347 :
1348 0 : }
1349 :
1350 : /**********************************************************************
1351 : * TABRelation::Init()
1352 : *
1353 : * Set the details of the relation: the main and related tables, the fields
1354 : * through which they will be connected, and the list of fields to select.
1355 : * After this call, we are ready to read data records.
1356 : *
1357 : * For write access, Init() is called with pszMain/RelFieldName and
1358 : * **papszSelectedFields passed as NULL. They will have to be set through
1359 : * other methods before a first feature can be written.
1360 : *
1361 : * A new OGRFeatureDefn is also built for the combined tables.
1362 : *
1363 : * Returns 0 on success, or -1 or error.
1364 : **********************************************************************/
1365 0 : int TABRelation::Init(const char *pszViewName,
1366 : TABFile *poMainTable, TABFile *poRelTable,
1367 : const char *pszMainFieldName,
1368 : const char *pszRelFieldName,
1369 : char **papszSelectedFields)
1370 : {
1371 0 : if (poMainTable == NULL || poRelTable == NULL)
1372 0 : return -1;
1373 :
1374 : // We'll need the feature Defn later...
1375 : OGRFeatureDefn *poMainDefn, *poRelDefn;
1376 :
1377 0 : poMainDefn = poMainTable->GetLayerDefn();
1378 0 : poRelDefn = poRelTable->GetLayerDefn();
1379 :
1380 : /*-----------------------------------------------------------------
1381 : * Keep info for later use about source tables, etc.
1382 : *----------------------------------------------------------------*/
1383 0 : ResetAllMembers();
1384 :
1385 0 : m_poMainTable = poMainTable;
1386 0 : if (pszMainFieldName)
1387 : {
1388 0 : m_pszMainFieldName = CPLStrdup(pszMainFieldName);
1389 0 : m_nMainFieldNo = poMainDefn->GetFieldIndex(pszMainFieldName);
1390 : }
1391 :
1392 0 : m_poRelTable = poRelTable;
1393 0 : if (pszRelFieldName)
1394 : {
1395 0 : m_pszRelFieldName = CPLStrdup(pszRelFieldName);
1396 0 : m_nRelFieldNo = poRelDefn->GetFieldIndex(pszRelFieldName);
1397 0 : m_nRelFieldIndexNo = poRelTable->GetFieldIndexNumber(m_nRelFieldNo);
1398 0 : m_poRelINDFileRef = poRelTable->GetINDFileRef();
1399 :
1400 0 : if (m_nRelFieldIndexNo >= 0 && m_poRelINDFileRef == NULL)
1401 : {
1402 : CPLError(CE_Failure, CPLE_FileIO,
1403 : "Field %s is indexed but the .IND file is missing.",
1404 0 : pszRelFieldName);
1405 0 : return -1;
1406 : }
1407 : }
1408 :
1409 : /*-----------------------------------------------------------------
1410 : * Init field maps. For each field in each table, a -1 means that
1411 : * the field is not selected, and a value >=0 is the index of the
1412 : * field in the view's FeatureDefn
1413 : *----------------------------------------------------------------*/
1414 : int i;
1415 0 : int numFields1 = (poMainDefn?poMainDefn->GetFieldCount():0);
1416 0 : int numFields2 = (poRelDefn?poRelDefn->GetFieldCount():0);
1417 :
1418 0 : m_panMainTableFieldMap = (int*)CPLMalloc((numFields1+1)*sizeof(int));
1419 0 : for(i=0; i<numFields1; i++)
1420 0 : m_panMainTableFieldMap[i] = -1;
1421 0 : m_panRelTableFieldMap = (int*)CPLMalloc((numFields2+1)*sizeof(int));
1422 0 : for(i=0; i<numFields2; i++)
1423 0 : m_panRelTableFieldMap[i] = -1;
1424 :
1425 : /*-----------------------------------------------------------------
1426 : * If selectedFields = "*" then select all fields from both tables
1427 : *----------------------------------------------------------------*/
1428 0 : if (CSLCount(papszSelectedFields) == 1 &&
1429 0 : EQUAL(papszSelectedFields[0], "*") )
1430 : {
1431 0 : CSLDestroy(papszSelectedFields);
1432 0 : papszSelectedFields = NULL;
1433 :
1434 0 : for(i=0; i<numFields1; i++)
1435 : {
1436 0 : OGRFieldDefn *poFieldDefn = poMainDefn->GetFieldDefn(i);
1437 :
1438 : papszSelectedFields = CSLAddString(papszSelectedFields,
1439 0 : poFieldDefn->GetNameRef());
1440 : }
1441 :
1442 0 : for(i=0; i<numFields2; i++)
1443 : {
1444 0 : OGRFieldDefn *poFieldDefn = poRelDefn->GetFieldDefn(i);
1445 :
1446 0 : if (CSLFindString(papszSelectedFields,
1447 : poFieldDefn->GetNameRef()) != -1)
1448 0 : continue; // Avoid duplicate field name in view
1449 :
1450 : papszSelectedFields = CSLAddString(papszSelectedFields,
1451 0 : poFieldDefn->GetNameRef());
1452 : }
1453 :
1454 : }
1455 :
1456 : /*-----------------------------------------------------------------
1457 : * Create new FeatureDefn and copy selected fields definitions
1458 : * while updating the appropriate field maps.
1459 : *----------------------------------------------------------------*/
1460 0 : int nIndex, numSelFields = CSLCount(papszSelectedFields);
1461 : OGRFieldDefn *poFieldDefn;
1462 :
1463 0 : m_poDefn = new OGRFeatureDefn(pszViewName);
1464 : // Ref count defaults to 0... set it to 1
1465 0 : m_poDefn->Reference();
1466 :
1467 0 : for(i=0; i<numSelFields ; i++)
1468 : {
1469 0 : if (poMainDefn &&
1470 0 : (nIndex=poMainDefn->GetFieldIndex(papszSelectedFields[i])) >=0)
1471 : {
1472 : /* Field from the main table
1473 : */
1474 0 : poFieldDefn = poMainDefn->GetFieldDefn(nIndex);
1475 0 : m_poDefn->AddFieldDefn(poFieldDefn);
1476 0 : m_panMainTableFieldMap[nIndex] = m_poDefn->GetFieldCount()-1;
1477 : }
1478 0 : else if (poRelDefn &&
1479 0 : (nIndex=poRelDefn->GetFieldIndex(papszSelectedFields[i]))>=0)
1480 : {
1481 : /* Field from the related table
1482 : */
1483 0 : poFieldDefn = poRelDefn->GetFieldDefn(nIndex);
1484 0 : m_poDefn->AddFieldDefn(poFieldDefn);
1485 0 : m_panRelTableFieldMap[nIndex] = m_poDefn->GetFieldCount()-1;
1486 : }
1487 : else
1488 : {
1489 : // Hummm... field does not exist... likely an unsupported feature!
1490 : // At least send a warning and ignore the field.
1491 : CPLError(CE_Warning, CPLE_IllegalArg,
1492 : "Selected Field %s not found in source tables %s and %s",
1493 : papszSelectedFields[i],
1494 0 : poMainDefn->GetName(), poRelDefn->GetName());
1495 : }
1496 : }
1497 :
1498 0 : return 0;
1499 : }
1500 :
1501 :
1502 : /**********************************************************************
1503 : * TABRelation::CreateRelFields()
1504 : *
1505 : * For write access, create the integer fields in each table that will
1506 : * link them, and setup everything to be ready to write the first feature.
1507 : *
1508 : * This function should be called just before writing the first feature.
1509 : *
1510 : * Returns 0 on success, or -1 or error.
1511 : **********************************************************************/
1512 0 : int TABRelation::CreateRelFields()
1513 : {
1514 : int i;
1515 :
1516 : /*-----------------------------------------------------------------
1517 : * Create the field in each table.
1518 : * The default name is "MI_refnum" but if a field with the same name
1519 : * already exists then we'll try to generate a unique name.
1520 : *----------------------------------------------------------------*/
1521 0 : m_pszMainFieldName = CPLStrdup("MI_Refnum ");
1522 0 : strcpy(m_pszMainFieldName, "MI_Refnum");
1523 0 : i = 1;
1524 0 : while(m_poDefn->GetFieldIndex(m_pszMainFieldName) >= 0)
1525 : {
1526 0 : sprintf(m_pszMainFieldName, "MI_Refnum_%d", i++);
1527 : }
1528 0 : m_pszRelFieldName = CPLStrdup(m_pszMainFieldName);
1529 :
1530 0 : m_nMainFieldNo = m_nRelFieldNo = -1;
1531 0 : if (m_poMainTable->AddFieldNative(m_pszMainFieldName,
1532 0 : TABFInteger, 0, 0) == 0)
1533 0 : m_nMainFieldNo = m_poMainTable->GetLayerDefn()->GetFieldCount()-1;
1534 :
1535 0 : if (m_poRelTable->AddFieldNative(m_pszRelFieldName,
1536 0 : TABFInteger, 0, 0) == 0)
1537 0 : m_nRelFieldNo = m_poRelTable->GetLayerDefn()->GetFieldCount()-1;
1538 :
1539 0 : if (m_nMainFieldNo == -1 || m_nRelFieldNo == -1)
1540 0 : return -1;
1541 :
1542 0 : if (m_poMainTable->SetFieldIndexed(m_nMainFieldNo) == -1)
1543 0 : return -1;
1544 :
1545 0 : if ((m_nRelFieldIndexNo=m_poRelTable->SetFieldIndexed(m_nRelFieldNo)) ==-1)
1546 0 : return -1;
1547 :
1548 0 : m_poRelINDFileRef = m_poRelTable->GetINDFileRef();
1549 :
1550 : /*-----------------------------------------------------------------
1551 : * Update field maps
1552 : *----------------------------------------------------------------*/
1553 : OGRFeatureDefn *poMainDefn, *poRelDefn;
1554 :
1555 0 : poMainDefn = m_poMainTable->GetLayerDefn();
1556 0 : poRelDefn = m_poRelTable->GetLayerDefn();
1557 :
1558 : m_panMainTableFieldMap = (int*)CPLRealloc(m_panMainTableFieldMap,
1559 0 : poMainDefn->GetFieldCount()*sizeof(int));
1560 0 : m_panMainTableFieldMap[poMainDefn->GetFieldCount()-1] = -1;
1561 :
1562 : m_panRelTableFieldMap = (int*)CPLRealloc(m_panRelTableFieldMap,
1563 0 : poRelDefn->GetFieldCount()*sizeof(int));
1564 0 : m_panRelTableFieldMap[poRelDefn->GetFieldCount()-1] = -1;
1565 :
1566 : /*-----------------------------------------------------------------
1567 : * Make sure the first unique field (in poRelTable) is indexed since
1568 : * it is the one against which we will try to match records.
1569 : *----------------------------------------------------------------*/
1570 0 : if ( m_poRelTable->SetFieldIndexed(0) == -1)
1571 0 : return -1;
1572 :
1573 0 : return 0;
1574 : }
1575 :
1576 : /**********************************************************************
1577 : * TABRelation::GetFeature()
1578 : *
1579 : * Fill and return a TABFeature object for the specified feature id.
1580 : *
1581 : * The retuned pointer is a new TABFeature that will have to be freed
1582 : * by the caller.
1583 : *
1584 : * Returns NULL if the specified feature id does not exist of if an
1585 : * error happened. In any case, CPLError() will have been called to
1586 : * report the reason of the failure.
1587 : *
1588 : * __TODO__ The current implementation fetches the features from each table
1589 : * and creates a 3rd feature to merge them. There would be room for
1590 : * optimization, at least by avoiding the duplication of the geometry
1591 : * which can be big sometimes... but this would imply changes at the
1592 : * lower-level in the lib. and we won't go there yet.
1593 : **********************************************************************/
1594 0 : TABFeature *TABRelation::GetFeature(int nFeatureId)
1595 : {
1596 : TABFeature *poMainFeature;
1597 : TABFeature *poCurFeature;
1598 :
1599 : /*-----------------------------------------------------------------
1600 : * Make sure init() has been called
1601 : *----------------------------------------------------------------*/
1602 0 : if (m_poMainTable == NULL || m_poRelTable == NULL)
1603 : {
1604 : CPLError(CE_Failure, CPLE_IllegalArg,
1605 0 : "GetFeatureRef() failed: object not initialized yet!");
1606 0 : return NULL;
1607 : }
1608 :
1609 : /*-----------------------------------------------------------------
1610 : * Read main feature and create a new one of the right type
1611 : *----------------------------------------------------------------*/
1612 0 : if ((poMainFeature = m_poMainTable->GetFeatureRef(nFeatureId)) == NULL)
1613 : {
1614 : // Feature cannot be read from main table...
1615 : // an error has already been reported.
1616 0 : return NULL;
1617 : }
1618 :
1619 0 : poCurFeature = poMainFeature->CloneTABFeature(m_poDefn);
1620 :
1621 : /*-----------------------------------------------------------------
1622 : * Keep track of FID and copy the geometry
1623 : *----------------------------------------------------------------*/
1624 0 : poCurFeature->SetFID(nFeatureId);
1625 :
1626 0 : if (poCurFeature->GetFeatureClass() != TABFCNoGeomFeature)
1627 : {
1628 : OGRGeometry *poGeom;
1629 0 : poGeom = poMainFeature->GetGeometryRef();
1630 0 : poCurFeature->SetGeometry(poGeom);
1631 : }
1632 :
1633 : /*-----------------------------------------------------------------
1634 : * Fetch feature from related table
1635 : *
1636 : * __TODO__ Right now we support only many-to-1 relationships, but
1637 : * it might be possible to have several related entries
1638 : * for a single key, and in this case we should return
1639 : * one new feature for each of them.
1640 : *----------------------------------------------------------------*/
1641 0 : TABFeature *poRelFeature=NULL;
1642 : GByte *pKey = BuildFieldKey(poMainFeature, m_nMainFieldNo,
1643 0 : m_poMainTable->GetNativeFieldType(m_nMainFieldNo),
1644 0 : m_nRelFieldIndexNo);
1645 : int i;
1646 0 : int nRelFeatureId = m_poRelINDFileRef->FindFirst(m_nRelFieldIndexNo, pKey);
1647 :
1648 0 : if (nRelFeatureId > 0)
1649 0 : poRelFeature = m_poRelTable->GetFeatureRef(nRelFeatureId);
1650 :
1651 : /*-----------------------------------------------------------------
1652 : * Copy fields from poMainFeature
1653 : *----------------------------------------------------------------*/
1654 0 : for(i=0; i<poMainFeature->GetFieldCount(); i++)
1655 : {
1656 0 : if (m_panMainTableFieldMap[i] != -1)
1657 : {
1658 : poCurFeature->SetField(m_panMainTableFieldMap[i],
1659 0 : poMainFeature->GetRawFieldRef(i));
1660 : }
1661 : }
1662 :
1663 : /*-----------------------------------------------------------------
1664 : * Copy fields from poRelFeature...
1665 : *
1666 : * NOTE: For now, if no corresponding feature is found in RelTable
1667 : * then we will just leave the corresponding fields unset.
1668 : *----------------------------------------------------------------*/
1669 0 : for(i=0; poRelFeature && i<poRelFeature->GetFieldCount(); i++)
1670 : {
1671 0 : if (m_panRelTableFieldMap[i] != -1)
1672 : {
1673 : poCurFeature->SetField(m_panRelTableFieldMap[i],
1674 0 : poRelFeature->GetRawFieldRef(i));
1675 : }
1676 : }
1677 :
1678 0 : return poCurFeature;
1679 : }
1680 :
1681 :
1682 :
1683 : /**********************************************************************
1684 : * TABRelation::BuildFieldKey()
1685 : *
1686 : * Return the index key for the specified field in poFeature.
1687 : * Simply maps the call to the proper method in the TABINDFile class.
1688 : *
1689 : * Returns a reference to a TABINDFile internal buffer that should not
1690 : * be freed by the caller.
1691 : **********************************************************************/
1692 0 : GByte *TABRelation::BuildFieldKey(TABFeature *poFeature, int nFieldNo,
1693 : TABFieldType eType, int nIndexNo)
1694 : {
1695 0 : GByte *pKey = NULL;
1696 :
1697 0 : switch(eType)
1698 : {
1699 : case TABFChar:
1700 : pKey = m_poRelINDFileRef->BuildKey(nIndexNo,
1701 0 : poFeature->GetFieldAsString(nFieldNo));
1702 0 : break;
1703 :
1704 : case TABFDecimal:
1705 : case TABFFloat:
1706 : pKey = m_poRelINDFileRef->BuildKey(nIndexNo,
1707 0 : poFeature->GetFieldAsDouble(nFieldNo));
1708 0 : break;
1709 :
1710 : // __TODO__ DateTime fields are 8 bytes long, not supported yet by
1711 : // the indexing code (see bug #1844).
1712 : case TABFDateTime:
1713 : CPLError(CE_Failure, CPLE_NotSupported,
1714 0 : "TABRelation on field of type DateTime not supported yet.");
1715 0 : break;
1716 :
1717 : case TABFInteger:
1718 : case TABFSmallInt:
1719 : case TABFDate:
1720 : case TABFTime:
1721 : case TABFLogical:
1722 : default:
1723 : pKey = m_poRelINDFileRef->BuildKey(nIndexNo,
1724 0 : poFeature->GetFieldAsInteger(nFieldNo));
1725 : break;
1726 : }
1727 :
1728 0 : return pKey;
1729 : }
1730 :
1731 :
1732 : /**********************************************************************
1733 : * TABRelation::GetNativeFieldType()
1734 : *
1735 : * Returns the native MapInfo field type for the specified field.
1736 : *
1737 : * Returns TABFUnknown if file is not opened, or if specified field index is
1738 : * invalid.
1739 : *
1740 : * Note that field ids are positive and start at 0.
1741 : **********************************************************************/
1742 0 : TABFieldType TABRelation::GetNativeFieldType(int nFieldId)
1743 : {
1744 : int i, numFields;
1745 :
1746 0 : if (m_poMainTable==NULL || m_poRelTable==NULL ||
1747 : m_panMainTableFieldMap==NULL || m_panRelTableFieldMap==NULL)
1748 0 : return TABFUnknown;
1749 :
1750 : /*-----------------------------------------------------------------
1751 : * Look for nFieldId in the field maps and call the corresponding
1752 : * TAB file's GetNativeFieldType()
1753 : *----------------------------------------------------------------*/
1754 0 : numFields = m_poMainTable->GetLayerDefn()->GetFieldCount();
1755 0 : for(i=0; i<numFields; i++)
1756 : {
1757 0 : if (m_panMainTableFieldMap[i] == nFieldId)
1758 : {
1759 0 : return m_poMainTable->GetNativeFieldType(i);
1760 : }
1761 : }
1762 :
1763 0 : numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1764 0 : for(i=0; i<numFields; i++)
1765 : {
1766 0 : if (m_panRelTableFieldMap[i] == nFieldId)
1767 : {
1768 0 : return m_poRelTable->GetNativeFieldType(i);
1769 : }
1770 : }
1771 :
1772 0 : return TABFUnknown;
1773 : }
1774 :
1775 :
1776 : /**********************************************************************
1777 : * TABRelation::AddFieldNative()
1778 : *
1779 : * Create a new field using a native mapinfo data type... this is an
1780 : * alternative to defining fields through the OGR interface.
1781 : * This function should be called after creating a new dataset, but before
1782 : * writing the first feature.
1783 : *
1784 : * This function will build/update the OGRFeatureDefn that will have to be
1785 : * used when writing features to this dataset.
1786 : *
1787 : * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn().
1788 : *
1789 : * Returns 0 on success, -1 on error.
1790 : **********************************************************************/
1791 0 : int TABRelation::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
1792 : int nWidth /*=0*/, int nPrecision /*=0*/,
1793 : GBool bIndexed /*=FALSE*/, GBool bUnique/*=FALSE*/, int bApproxOK)
1794 : {
1795 0 : if (m_poMainTable==NULL || m_poRelTable==NULL ||
1796 : m_panMainTableFieldMap==NULL || m_panRelTableFieldMap==NULL)
1797 0 : return -1;
1798 :
1799 0 : if (!bUnique)
1800 : {
1801 : /*-------------------------------------------------------------
1802 : * Add field to poMainTable and to m_poDefn
1803 : *------------------------------------------------------------*/
1804 0 : if (m_poMainTable->AddFieldNative(pszName, eMapInfoType,
1805 : nWidth, nPrecision,
1806 0 : bIndexed, bUnique, bApproxOK) != 0)
1807 0 : return -1;
1808 :
1809 0 : OGRFeatureDefn *poMainDefn = m_poMainTable->GetLayerDefn();
1810 :
1811 : m_panMainTableFieldMap = (int*)CPLRealloc(m_panMainTableFieldMap,
1812 0 : poMainDefn->GetFieldCount()*sizeof(int));
1813 :
1814 : m_poDefn->AddFieldDefn(poMainDefn->GetFieldDefn(poMainDefn->
1815 0 : GetFieldCount()-1));
1816 :
1817 0 : m_panMainTableFieldMap[poMainDefn->GetFieldCount()-1] =
1818 0 : m_poDefn->GetFieldCount()-1;
1819 : }
1820 : else
1821 : {
1822 : /*-------------------------------------------------------------
1823 : * Add field to poRelTable and to m_poDefn
1824 : *------------------------------------------------------------*/
1825 0 : if (m_poRelTable->AddFieldNative(pszName, eMapInfoType,
1826 : nWidth, nPrecision,
1827 0 : bIndexed, bUnique, bApproxOK) != 0)
1828 0 : return -1;
1829 :
1830 0 : OGRFeatureDefn *poRelDefn = m_poRelTable->GetLayerDefn();
1831 :
1832 : m_panRelTableFieldMap = (int*)CPLRealloc(m_panRelTableFieldMap,
1833 0 : poRelDefn->GetFieldCount()*sizeof(int));
1834 :
1835 : m_poDefn->AddFieldDefn(poRelDefn->GetFieldDefn(poRelDefn->
1836 0 : GetFieldCount()-1));
1837 :
1838 0 : m_panRelTableFieldMap[poRelDefn->GetFieldCount()-1] =
1839 0 : m_poDefn->GetFieldCount()-1;
1840 :
1841 : // The first field in this table must be indexed.
1842 0 : if (poRelDefn->GetFieldCount() == 1)
1843 0 : m_poRelTable->SetFieldIndexed(0);
1844 : }
1845 :
1846 0 : return 0;
1847 : }
1848 :
1849 :
1850 : /**********************************************************************
1851 : * TABRelation::IsFieldIndexed()
1852 : *
1853 : * Returns TRUE is specified field is indexed.
1854 : *
1855 : * Note that field ids are positive and start at 0.
1856 : **********************************************************************/
1857 0 : GBool TABRelation::IsFieldIndexed(int nFieldId)
1858 : {
1859 : int i, numFields;
1860 :
1861 0 : if (m_poMainTable==NULL || m_poRelTable==NULL ||
1862 : m_panMainTableFieldMap==NULL || m_panRelTableFieldMap==NULL)
1863 0 : return FALSE;
1864 :
1865 : /*-----------------------------------------------------------------
1866 : * Look for nFieldId in the field maps and call the corresponding
1867 : * TAB file's GetNativeFieldType()
1868 : *----------------------------------------------------------------*/
1869 0 : numFields = m_poMainTable->GetLayerDefn()->GetFieldCount();
1870 0 : for(i=0; i<numFields; i++)
1871 : {
1872 0 : if (m_panMainTableFieldMap[i] == nFieldId)
1873 : {
1874 0 : return m_poMainTable->IsFieldIndexed(i);
1875 : }
1876 : }
1877 :
1878 0 : numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1879 0 : for(i=0; i<numFields; i++)
1880 : {
1881 0 : if (m_panRelTableFieldMap[i] == nFieldId)
1882 : {
1883 0 : return m_poRelTable->IsFieldIndexed(i);
1884 : }
1885 : }
1886 :
1887 0 : return FALSE;
1888 : }
1889 :
1890 : /**********************************************************************
1891 : * TABRelation::SetFieldIndexed()
1892 : *
1893 : * Request that the specified field be indexed. This will create the .IND
1894 : * file, etc.
1895 : *
1896 : * Note that field ids are positive and start at 0.
1897 : *
1898 : * Returns 0 on success, -1 on error.
1899 : **********************************************************************/
1900 0 : int TABRelation::SetFieldIndexed(int nFieldId)
1901 : {
1902 : int i, numFields;
1903 :
1904 0 : if (m_poMainTable==NULL || m_poRelTable==NULL ||
1905 : m_panMainTableFieldMap==NULL || m_panRelTableFieldMap==NULL)
1906 0 : return -1;
1907 :
1908 : /*-----------------------------------------------------------------
1909 : * Look for nFieldId in the field maps and call the corresponding
1910 : * TAB file's GetNativeFieldType()
1911 : *----------------------------------------------------------------*/
1912 0 : numFields = m_poMainTable->GetLayerDefn()->GetFieldCount();
1913 0 : for(i=0; i<numFields; i++)
1914 : {
1915 0 : if (m_panMainTableFieldMap[i] == nFieldId)
1916 : {
1917 0 : return m_poMainTable->SetFieldIndexed(i);
1918 : }
1919 : }
1920 :
1921 0 : numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1922 0 : for(i=0; i<numFields; i++)
1923 : {
1924 0 : if (m_panRelTableFieldMap[i] == nFieldId)
1925 : {
1926 0 : return m_poRelTable->SetFieldIndexed(i);
1927 : }
1928 : }
1929 :
1930 0 : return -1;
1931 : }
1932 :
1933 : /**********************************************************************
1934 : * TABRelation::IsFieldUnique()
1935 : *
1936 : * Returns TRUE is specified field is part of the unique table (poRelTable).
1937 : *
1938 : * Note that field ids are positive and start at 0.
1939 : **********************************************************************/
1940 0 : GBool TABRelation::IsFieldUnique(int nFieldId)
1941 : {
1942 : int i, numFields;
1943 :
1944 0 : if (m_poMainTable==NULL || m_poRelTable==NULL ||
1945 : m_panMainTableFieldMap==NULL || m_panRelTableFieldMap==NULL)
1946 0 : return FALSE;
1947 :
1948 : /*-----------------------------------------------------------------
1949 : * Look for nFieldId in the poRelTable field map
1950 : *----------------------------------------------------------------*/
1951 0 : numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
1952 0 : for(i=0; i<numFields; i++)
1953 : {
1954 0 : if (m_panRelTableFieldMap[i] == nFieldId)
1955 : {
1956 0 : return TRUE; // If it's here then it is unique!
1957 : }
1958 : }
1959 :
1960 0 : return FALSE;
1961 : }
1962 :
1963 : /**********************************************************************
1964 : * TABRelation::WriteFeature()
1965 : *
1966 : * Write a feature to this dataset.
1967 : *
1968 : * For now only sequential writes are supported (i.e. with nFeatureId=-1)
1969 : * but eventually we should be able to do random access by specifying
1970 : * a value through nFeatureId.
1971 : *
1972 : * Returns the new featureId (> 0) on success, or -1 if an
1973 : * error happened in which case, CPLError() will have been called to
1974 : * report the reason of the failure.
1975 : **********************************************************************/
1976 0 : int TABRelation::WriteFeature(TABFeature *poFeature, int nFeatureId /*=-1*/)
1977 : {
1978 0 : TABFeature *poMainFeature=NULL;
1979 :
1980 0 : if (nFeatureId != -1)
1981 : {
1982 : CPLError(CE_Failure, CPLE_NotSupported,
1983 0 : "WriteFeature(): random access not implemented yet.");
1984 0 : return -1;
1985 : }
1986 :
1987 0 : CPLAssert(m_poMainTable && m_poRelTable);
1988 :
1989 : // We'll need the feature Defn later...
1990 : OGRFeatureDefn *poMainDefn, *poRelDefn;
1991 :
1992 0 : poMainDefn = m_poMainTable->GetLayerDefn();
1993 0 : poRelDefn = m_poRelTable->GetLayerDefn();
1994 :
1995 : /*-----------------------------------------------------------------
1996 : * Create one feature for each table
1997 : * Copy the geometry only to the feature from the main table
1998 : *----------------------------------------------------------------*/
1999 0 : poMainFeature = poFeature->CloneTABFeature(poMainDefn);
2000 :
2001 0 : if (poFeature->GetFeatureClass() != TABFCNoGeomFeature)
2002 : {
2003 : OGRGeometry *poGeom;
2004 0 : poGeom = poFeature->GetGeometryRef();
2005 0 : poMainFeature->SetGeometry(poGeom);
2006 : }
2007 :
2008 : /*-----------------------------------------------------------------
2009 : * Copy fields to poMainFeature
2010 : *----------------------------------------------------------------*/
2011 0 : for(int i=0; i<poMainDefn->GetFieldCount(); i++)
2012 : {
2013 0 : if (m_panMainTableFieldMap[i] != -1)
2014 : {
2015 : poMainFeature->SetField(i,
2016 0 : poFeature->GetRawFieldRef(m_panMainTableFieldMap[i]));
2017 : }
2018 : }
2019 :
2020 : /*-----------------------------------------------------------------
2021 : * Look for a record id for the unique fields, and write a new
2022 : * record if necessary
2023 : *----------------------------------------------------------------*/
2024 0 : int nRecordNo = 0;
2025 0 : int nUniqueIndexNo=-1;
2026 0 : if (m_panMainTableFieldMap[0] != -1)
2027 0 : nUniqueIndexNo =m_poRelTable->GetFieldIndexNumber( 0 );
2028 :
2029 0 : if (nUniqueIndexNo > 0)
2030 : {
2031 : GByte *pKey = BuildFieldKey(poFeature, 0,
2032 0 : m_poRelTable->GetNativeFieldType(0),
2033 0 : nUniqueIndexNo);
2034 :
2035 0 : if ((nRecordNo=m_poRelINDFileRef->FindFirst(nUniqueIndexNo, pKey))==-1)
2036 0 : return -1;
2037 :
2038 0 : if (nRecordNo == 0)
2039 : {
2040 : /*---------------------------------------------------------
2041 : * No record in poRelTable yet for this unique value...
2042 : * add one now...
2043 : *--------------------------------------------------------*/
2044 0 : TABFeature *poRelFeature = new TABFeature(poRelDefn);
2045 :
2046 0 : for(int i=0; i<poRelDefn->GetFieldCount(); i++)
2047 : {
2048 0 : if (m_panRelTableFieldMap[i] != -1)
2049 : {
2050 : poRelFeature->SetField(i,
2051 0 : poFeature->GetRawFieldRef(m_panRelTableFieldMap[i]));
2052 : }
2053 : }
2054 :
2055 0 : nRecordNo = ++m_nUniqueRecordNo;
2056 :
2057 0 : poRelFeature->SetField(m_nRelFieldNo, nRecordNo);
2058 :
2059 0 : if (m_poRelTable->CreateFeature(poRelFeature) == OGRERR_NONE)
2060 0 : return -1;
2061 :
2062 0 : delete poRelFeature;
2063 : }
2064 : }
2065 :
2066 :
2067 : /*-----------------------------------------------------------------
2068 : * Write poMainFeature to the main table
2069 : *----------------------------------------------------------------*/
2070 0 : poMainFeature->SetField(m_nMainFieldNo, nRecordNo);
2071 :
2072 0 : if (m_poMainTable->CreateFeature(poMainFeature) != OGRERR_NONE)
2073 0 : nFeatureId = poMainFeature->GetFID();
2074 : else
2075 0 : nFeatureId = -1;
2076 :
2077 0 : delete poMainFeature;
2078 :
2079 0 : return nFeatureId;
2080 : }
2081 :
2082 :
2083 : /**********************************************************************
2084 : * TABFile::SetFeatureDefn()
2085 : *
2086 : * NOT FULLY IMPLEMENTED YET...
2087 : *
2088 : * Returns 0 on success, -1 on error.
2089 : **********************************************************************/
2090 0 : int TABRelation::SetFeatureDefn(OGRFeatureDefn *poFeatureDefn,
2091 : TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */)
2092 : {
2093 0 : if (m_poDefn && m_poDefn->GetFieldCount() > 0)
2094 : {
2095 0 : CPLAssert(m_poDefn==NULL);
2096 0 : return -1;
2097 : }
2098 :
2099 : /*-----------------------------------------------------------------
2100 : * Keep a reference to the OGRFeatureDefn... we'll have to take the
2101 : * reference count into account when we are done with it.
2102 : *----------------------------------------------------------------*/
2103 0 : if (m_poDefn && m_poDefn->Dereference() == 0)
2104 0 : delete m_poDefn;
2105 :
2106 0 : m_poDefn = poFeatureDefn;
2107 0 : m_poDefn->Reference();
2108 :
2109 0 : return 0;
2110 : }
|