1 : /**********************************************************************
2 : * $Id: mitab_idfile.cpp,v 1.8 2006-11-28 18:49:08 dmorissette Exp $
3 : *
4 : * Name: mitab_idfile.cpp
5 : * Project: MapInfo TAB Read/Write library
6 : * Language: C++
7 : * Purpose: Implementation of the TABIDFile class used to handle
8 : * reading/writing of the .ID file attached to a .MAP file
9 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
10 : *
11 : **********************************************************************
12 : * Copyright (c) 1999, 2000, Daniel Morissette
13 : *
14 : * Permission is hereby granted, free of charge, to any person obtaining a
15 : * copy of this software and associated documentation files (the "Software"),
16 : * to deal in the Software without restriction, including without limitation
17 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 : * and/or sell copies of the Software, and to permit persons to whom the
19 : * Software is furnished to do so, subject to the following conditions:
20 : *
21 : * The above copyright notice and this permission notice shall be included
22 : * in all copies or substantial portions of the Software.
23 : *
24 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 : * DEALINGS IN THE SOFTWARE.
31 : **********************************************************************
32 : *
33 : * $Log: mitab_idfile.cpp,v $
34 : * Revision 1.8 2006-11-28 18:49:08 dmorissette
35 : * Completed changes to split TABMAPObjectBlocks properly and produce an
36 : * optimal spatial index (bug 1585)
37 : *
38 : * Revision 1.7 2004/06/30 20:29:04 dmorissette
39 : * Fixed refs to old address danmo@videotron.ca
40 : *
41 : * Revision 1.6 2000/01/18 22:08:56 daniel
42 : * Allow opening of 0-size .ID file (dataset with 0 features)
43 : *
44 : * Revision 1.5 2000/01/15 22:30:44 daniel
45 : * Switch to MIT/X-Consortium OpenSource license
46 : *
47 : * Revision 1.4 1999/09/26 14:59:36 daniel
48 : * Implemented write support
49 : *
50 : * Revision 1.3 1999/09/20 18:43:01 daniel
51 : * Use binary acces to open file.
52 : *
53 : * Revision 1.2 1999/09/16 02:39:16 daniel
54 : * Completed read support for most feature types
55 : *
56 : * Revision 1.1 1999/07/12 04:18:24 daniel
57 : * Initial checkin
58 : *
59 : **********************************************************************/
60 :
61 : #include "mitab.h"
62 : #include "mitab_utils.h"
63 :
64 : /*=====================================================================
65 : * class TABIDFile
66 : *====================================================================*/
67 :
68 :
69 : /**********************************************************************
70 : * TABIDFile::TABIDFile()
71 : *
72 : * Constructor.
73 : **********************************************************************/
74 6 : TABIDFile::TABIDFile()
75 : {
76 6 : m_fp = NULL;
77 6 : m_pszFname = NULL;
78 6 : m_poIDBlock = NULL;
79 6 : m_nMaxId = -1;
80 6 : }
81 :
82 : /**********************************************************************
83 : * TABIDFile::~TABIDFile()
84 : *
85 : * Destructor.
86 : **********************************************************************/
87 6 : TABIDFile::~TABIDFile()
88 : {
89 6 : Close();
90 6 : }
91 :
92 : /**********************************************************************
93 : * TABIDFile::Open()
94 : *
95 : * Open a .ID file, and initialize the structures to be ready to read
96 : * objects from it.
97 : *
98 : * If the filename that is passed in contains a .MAP extension then
99 : * the extension will be changed to .ID before trying to open the file.
100 : *
101 : * Returns 0 on success, -1 on error.
102 : **********************************************************************/
103 6 : int TABIDFile::Open(const char *pszFname, const char *pszAccess)
104 : {
105 : int nLen;
106 :
107 6 : if (m_fp)
108 : {
109 : CPLError(CE_Failure, CPLE_FileIO,
110 0 : "Open() failed: object already contains an open file");
111 0 : return -1;
112 : }
113 :
114 : /*-----------------------------------------------------------------
115 : * Validate access mode and make sure we use binary access.
116 : * Note that in Write mode we need TABReadWrite since we do random
117 : * updates in the index as data blocks are split
118 : *----------------------------------------------------------------*/
119 6 : if (EQUALN(pszAccess, "r", 1))
120 : {
121 3 : m_eAccessMode = TABRead;
122 3 : pszAccess = "rb";
123 : }
124 3 : else if (EQUALN(pszAccess, "w", 1))
125 : {
126 3 : m_eAccessMode = TABReadWrite;
127 3 : pszAccess = "wb+";
128 : }
129 : else
130 : {
131 : CPLError(CE_Failure, CPLE_FileIO,
132 0 : "Open() failed: access mode \"%s\" not supported", pszAccess);
133 0 : return -1;
134 : }
135 :
136 : /*-----------------------------------------------------------------
137 : * Change .MAP extension to .ID if necessary
138 : *----------------------------------------------------------------*/
139 6 : m_pszFname = CPLStrdup(pszFname);
140 :
141 6 : nLen = strlen(m_pszFname);
142 6 : if (nLen > 4 && strcmp(m_pszFname+nLen-4, ".MAP")==0)
143 0 : strcpy(m_pszFname+nLen-4, ".ID");
144 6 : else if (nLen > 4 && strcmp(m_pszFname+nLen-4, ".map")==0)
145 6 : strcpy(m_pszFname+nLen-4, ".id");
146 :
147 : /*-----------------------------------------------------------------
148 : * Change .MAP extension to .ID if necessary
149 : *----------------------------------------------------------------*/
150 : #ifndef _WIN32
151 6 : TABAdjustFilenameExtension(m_pszFname);
152 : #endif
153 :
154 : /*-----------------------------------------------------------------
155 : * Open file
156 : *----------------------------------------------------------------*/
157 6 : m_fp = VSIFOpen(m_pszFname, pszAccess);
158 :
159 6 : if (m_fp == NULL)
160 : {
161 : CPLError(CE_Failure, CPLE_FileIO,
162 0 : "Open() failed for %s", m_pszFname);
163 0 : CPLFree(m_pszFname);
164 0 : m_pszFname = NULL;
165 0 : return -1;
166 : }
167 :
168 6 : if (m_eAccessMode == TABRead)
169 : {
170 : /*-------------------------------------------------------------
171 : * READ access:
172 : * Establish the number of object IDs from the size of the file
173 : *------------------------------------------------------------*/
174 : VSIStatBuf sStatBuf;
175 3 : if ( VSIStat(m_pszFname, &sStatBuf) == -1 )
176 : {
177 : CPLError(CE_Failure, CPLE_FileIO,
178 0 : "stat() failed for %s\n", m_pszFname);
179 0 : Close();
180 0 : return -1;
181 : }
182 :
183 3 : m_nMaxId = sStatBuf.st_size/4;
184 3 : m_nBlockSize = MIN(1024, m_nMaxId*4);
185 :
186 : /*-------------------------------------------------------------
187 : * Read the first block from the file
188 : *------------------------------------------------------------*/
189 3 : m_poIDBlock = new TABRawBinBlock(m_eAccessMode, FALSE);
190 :
191 3 : if (m_nMaxId == 0)
192 : {
193 : // .ID file size = 0 ... just allocate a blank block but
194 : // it won't get really used anyways.
195 0 : m_nBlockSize = 512;
196 0 : m_poIDBlock->InitNewBlock(m_fp, m_nBlockSize, 0);
197 : }
198 3 : else if (m_poIDBlock->ReadFromFile(m_fp, 0, m_nBlockSize) != 0)
199 : {
200 : // CPLError() has already been called.
201 0 : Close();
202 0 : return -1;
203 : }
204 : }
205 : else
206 : {
207 : /*-------------------------------------------------------------
208 : * WRITE access:
209 : * Get ready to write to the file
210 : *------------------------------------------------------------*/
211 3 : m_poIDBlock = new TABRawBinBlock(m_eAccessMode, FALSE);
212 3 : m_nMaxId = 0;
213 3 : m_nBlockSize = 1024;
214 3 : m_poIDBlock->InitNewBlock(m_fp, m_nBlockSize, 0);
215 : }
216 :
217 6 : return 0;
218 : }
219 :
220 : /**********************************************************************
221 : * TABIDFile::Close()
222 : *
223 : * Close current file, and release all memory used.
224 : *
225 : * Returns 0 on success, -1 on error.
226 : **********************************************************************/
227 12 : int TABIDFile::Close()
228 : {
229 12 : if (m_fp == NULL)
230 6 : return 0;
231 :
232 : /*----------------------------------------------------------------
233 : * Write access: commit latest changes to the file.
234 : *---------------------------------------------------------------*/
235 6 : if (m_eAccessMode == TABReadWrite && m_poIDBlock)
236 : {
237 3 : m_poIDBlock->CommitToFile();
238 : }
239 :
240 : // Delete all structures
241 6 : delete m_poIDBlock;
242 6 : m_poIDBlock = NULL;
243 :
244 : // Close file
245 6 : VSIFClose(m_fp);
246 6 : m_fp = NULL;
247 :
248 6 : CPLFree(m_pszFname);
249 6 : m_pszFname = NULL;
250 :
251 6 : return 0;
252 : }
253 :
254 :
255 : /**********************************************************************
256 : * TABIDFile::GetObjPtr()
257 : *
258 : * Return the offset in the .MAP file where the map object with the
259 : * specified id is located.
260 : *
261 : * Note that object ids are positive and start at 1.
262 : *
263 : * An object Id of '0' means that object has no geometry.
264 : *
265 : * Returns a value >= 0 on success, -1 on error.
266 : **********************************************************************/
267 43 : GInt32 TABIDFile::GetObjPtr(GInt32 nObjId)
268 : {
269 43 : if (m_poIDBlock == NULL)
270 0 : return -1;
271 :
272 43 : if (nObjId < 1 || nObjId > m_nMaxId)
273 : {
274 : CPLError(CE_Failure, CPLE_IllegalArg,
275 : "GetObjPtr(): Invalid object ID %d (valid range is [1..%d])",
276 0 : nObjId, m_nMaxId);
277 0 : return -1;
278 : }
279 :
280 43 : if (m_poIDBlock->GotoByteInFile( (nObjId-1)*4 ) != 0)
281 0 : return -1;
282 :
283 43 : return m_poIDBlock->ReadInt32();
284 : }
285 :
286 : /**********************************************************************
287 : * TABIDFile::SetObjPtr()
288 : *
289 : * Set the offset in the .MAP file where the map object with the
290 : * specified id is located.
291 : *
292 : * Note that object ids are positive and start at 1.
293 : *
294 : * An object Id of '0' means that object has no geometry.
295 : *
296 : * Returns a value of 0 on success, -1 on error.
297 : **********************************************************************/
298 14 : int TABIDFile::SetObjPtr(GInt32 nObjId, GInt32 nObjPtr)
299 : {
300 14 : if (m_poIDBlock == NULL)
301 0 : return -1;
302 :
303 14 : if (m_eAccessMode != TABReadWrite)
304 : {
305 : CPLError(CE_Failure, CPLE_NotSupported,
306 0 : "SetObjPtr() can be used only with Write access.");
307 0 : return -1;
308 : }
309 :
310 14 : if (nObjId < 1)
311 : {
312 : CPLError(CE_Failure, CPLE_IllegalArg,
313 : "SetObjPtr(): Invalid object ID %d (must be greater than zero)",
314 0 : nObjId);
315 0 : return -1;
316 : }
317 :
318 : /*-----------------------------------------------------------------
319 : * GotoByteInFile() will automagically commit current block and init
320 : * a new one if necessary.
321 : *----------------------------------------------------------------*/
322 14 : GInt32 nLastIdBlock = ((m_nMaxId-1)*4) / m_nBlockSize;
323 14 : GInt32 nTargetIdBlock = ((nObjId-1)*4) / m_nBlockSize;
324 25 : if (m_nMaxId > 0 && nTargetIdBlock <= nLastIdBlock)
325 : {
326 : /* Pass second arg to GotoByteInFile() to force reading from file
327 : * when going back to blocks already committed
328 : */
329 11 : if (m_poIDBlock->GotoByteInFile( (nObjId-1)*4, TRUE ) != 0)
330 0 : return -1;
331 : }
332 : else
333 : {
334 : /* If we reach EOF then a new empty block will have to be allocated
335 : */
336 3 : if (m_poIDBlock->GotoByteInFile( (nObjId-1)*4 ) != 0)
337 0 : return -1;
338 : }
339 :
340 14 : m_nMaxId = MAX(m_nMaxId, nObjId);
341 :
342 14 : return m_poIDBlock->WriteInt32(nObjPtr);
343 : }
344 :
345 :
346 : /**********************************************************************
347 : * TABIDFile::GetMaxObjId()
348 : *
349 : * Return the value of the biggest valid object id.
350 : *
351 : * Note that object ids are positive and start at 1.
352 : *
353 : * Returns a value >= 0 on success, -1 on error.
354 : **********************************************************************/
355 0 : GInt32 TABIDFile::GetMaxObjId()
356 : {
357 0 : return m_nMaxId;
358 : }
359 :
360 :
361 : /**********************************************************************
362 : * TABIDFile::Dump()
363 : *
364 : * Dump block contents... available only in DEBUG mode.
365 : **********************************************************************/
366 : #ifdef DEBUG
367 :
368 0 : void TABIDFile::Dump(FILE *fpOut /*=NULL*/)
369 : {
370 0 : if (fpOut == NULL)
371 0 : fpOut = stdout;
372 :
373 0 : fprintf(fpOut, "----- TABIDFile::Dump() -----\n");
374 :
375 0 : if (m_fp == NULL)
376 : {
377 0 : fprintf(fpOut, "File is not opened.\n");
378 : }
379 : else
380 : {
381 0 : fprintf(fpOut, "File is opened: %s\n", m_pszFname);
382 0 : fprintf(fpOut, "Current index block follows ...\n\n");
383 0 : m_poIDBlock->Dump(fpOut);
384 0 : fprintf(fpOut, "... end of index block.\n\n");
385 :
386 : }
387 :
388 0 : fflush(fpOut);
389 0 : }
390 :
391 : #endif // DEBUG
392 :
393 :
394 :
395 :
396 :
|