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