1 : /**********************************************************************
2 : * $Id: mitab_maptoolblock.cpp,v 1.7 2006/11/28 18:49:08 dmorissette Exp $
3 : *
4 : * Name: mitab_maptoollock.cpp
5 : * Project: MapInfo TAB Read/Write library
6 : * Language: C++
7 : * Purpose: Implementation of the TABMAPToolBlock class used to handle
8 : * reading/writing of the .MAP files' drawing tool blocks
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_maptoolblock.cpp,v $
34 : * Revision 1.7 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.6 2004/06/30 20:29:04 dmorissette
39 : * Fixed refs to old address danmo@videotron.ca
40 : *
41 : * Revision 1.5 2000/11/15 04:13:50 daniel
42 : * Fixed writing of TABMAPToolBlock to allocate a new block when full
43 : *
44 : * Revision 1.4 2000/02/28 17:03:30 daniel
45 : * Changed TABMAPBlockManager to TABBinBlockManager
46 : *
47 : * Revision 1.3 2000/01/15 22:30:44 daniel
48 : * Switch to MIT/X-Consortium OpenSource license
49 : *
50 : * Revision 1.2 1999/09/26 14:59:37 daniel
51 : * Implemented write support
52 : *
53 : * Revision 1.1 1999/09/16 02:39:17 daniel
54 : * Completed read support for most feature types
55 : *
56 : **********************************************************************/
57 :
58 : #include "mitab.h"
59 :
60 : /*=====================================================================
61 : * class TABMAPToolBlock
62 : *====================================================================*/
63 :
64 : #define MAP_TOOL_HEADER_SIZE 8
65 :
66 : /**********************************************************************
67 : * TABMAPToolBlock::TABMAPToolBlock()
68 : *
69 : * Constructor.
70 : **********************************************************************/
71 4 : TABMAPToolBlock::TABMAPToolBlock(TABAccess eAccessMode /*= TABRead*/):
72 4 : TABRawBinBlock(eAccessMode, TRUE)
73 : {
74 4 : m_nNextToolBlock = m_numDataBytes = 0;
75 :
76 4 : m_numBlocksInChain = 1; // Current block counts as 1
77 :
78 4 : m_poBlockManagerRef = NULL;
79 4 : }
80 :
81 : /**********************************************************************
82 : * TABMAPToolBlock::~TABMAPToolBlock()
83 : *
84 : * Destructor.
85 : **********************************************************************/
86 4 : TABMAPToolBlock::~TABMAPToolBlock()
87 : {
88 :
89 4 : }
90 :
91 :
92 : /**********************************************************************
93 : * TABMAPToolBlock::EndOfChain()
94 : *
95 : * Return TRUE if we reached the end of the last block in the chain
96 : * TABMAPToolBlocks, or FALSE if there is still data to be read from
97 : * this chain.
98 : **********************************************************************/
99 6 : GBool TABMAPToolBlock::EndOfChain()
100 : {
101 6 : if (m_pabyBuf &&
102 : (m_nCurPos < (m_numDataBytes+MAP_TOOL_HEADER_SIZE) ||
103 : m_nNextToolBlock > 0 ) )
104 : {
105 4 : return FALSE; // There is still data to be read.
106 : }
107 :
108 2 : return TRUE;
109 : }
110 :
111 : /**********************************************************************
112 : * TABMAPToolBlock::InitBlockFromData()
113 : *
114 : * Perform some initialization on the block after its binary data has
115 : * been set or changed (or loaded from a file).
116 : *
117 : * Returns 0 if succesful or -1 if an error happened, in which case
118 : * CPLError() will have been called.
119 : **********************************************************************/
120 : int TABMAPToolBlock::InitBlockFromData(GByte *pabyBuf,
121 : int nBlockSize, int nSizeUsed,
122 : GBool bMakeCopy /* = TRUE */,
123 : FILE *fpSrc /* = NULL */,
124 2 : int nOffset /* = 0 */)
125 : {
126 : int nStatus;
127 :
128 : /*-----------------------------------------------------------------
129 : * First of all, we must call the base class' InitBlockFromData()
130 : *----------------------------------------------------------------*/
131 : nStatus = TABRawBinBlock::InitBlockFromData(pabyBuf, nBlockSize, nSizeUsed,
132 2 : bMakeCopy, fpSrc, nOffset);
133 2 : if (nStatus != 0)
134 0 : return nStatus;
135 :
136 : /*-----------------------------------------------------------------
137 : * Validate block type
138 : *----------------------------------------------------------------*/
139 2 : if (m_nBlockType != TABMAP_TOOL_BLOCK)
140 : {
141 : CPLError(CE_Failure, CPLE_FileIO,
142 : "InitBlockFromData(): Invalid Block Type: got %d expected %d",
143 0 : m_nBlockType, TABMAP_TOOL_BLOCK);
144 0 : CPLFree(m_pabyBuf);
145 0 : m_pabyBuf = NULL;
146 0 : return -1;
147 : }
148 :
149 : /*-----------------------------------------------------------------
150 : * Init member variables
151 : *----------------------------------------------------------------*/
152 2 : GotoByteInBlock(0x002);
153 2 : m_numDataBytes = ReadInt16(); /* Excluding 8 bytes header */
154 :
155 2 : m_nNextToolBlock = ReadInt32();
156 :
157 : /*-----------------------------------------------------------------
158 : * The read ptr is now located at the beginning of the data part.
159 : *----------------------------------------------------------------*/
160 2 : GotoByteInBlock(MAP_TOOL_HEADER_SIZE);
161 :
162 2 : return 0;
163 : }
164 :
165 : /**********************************************************************
166 : * TABMAPToolBlock::CommitToFile()
167 : *
168 : * Commit the current state of the binary block to the file to which
169 : * it has been previously attached.
170 : *
171 : * This method makes sure all values are properly set in the map object
172 : * block header and then calls TABRawBinBlock::CommitToFile() to do
173 : * the actual writing to disk.
174 : *
175 : * Returns 0 if succesful or -1 if an error happened, in which case
176 : * CPLError() will have been called.
177 : **********************************************************************/
178 2 : int TABMAPToolBlock::CommitToFile()
179 : {
180 2 : int nStatus = 0;
181 :
182 2 : if ( m_pabyBuf == NULL )
183 : {
184 : CPLError(CE_Failure, CPLE_AssertionFailed,
185 0 : "CommitToFile(): Block has not been initialized yet!");
186 0 : return -1;
187 : }
188 :
189 : /*-----------------------------------------------------------------
190 : * Make sure 8 bytes block header is up to date.
191 : *----------------------------------------------------------------*/
192 2 : GotoByteInBlock(0x000);
193 :
194 2 : WriteInt16(TABMAP_TOOL_BLOCK); // Block type code
195 2 : WriteInt16(m_nSizeUsed - MAP_TOOL_HEADER_SIZE); // num. bytes used
196 2 : WriteInt32(m_nNextToolBlock);
197 :
198 2 : nStatus = CPLGetLastErrorNo();
199 :
200 : /*-----------------------------------------------------------------
201 : * OK, call the base class to write the block to disk.
202 : *----------------------------------------------------------------*/
203 2 : if (nStatus == 0)
204 2 : nStatus = TABRawBinBlock::CommitToFile();
205 :
206 2 : return nStatus;
207 : }
208 :
209 : /**********************************************************************
210 : * TABMAPToolBlock::InitNewBlock()
211 : *
212 : * Initialize a newly created block so that it knows to which file it
213 : * is attached, its block size, etc . and then perform any specific
214 : * initialization for this block type, including writing a default
215 : * block header, etc. and leave the block ready to receive data.
216 : *
217 : * This is an alternative to calling ReadFromFile() or InitBlockFromData()
218 : * that puts the block in a stable state without loading any initial
219 : * data in it.
220 : *
221 : * Returns 0 if succesful or -1 if an error happened, in which case
222 : * CPLError() will have been called.
223 : **********************************************************************/
224 : int TABMAPToolBlock::InitNewBlock(FILE *fpSrc, int nBlockSize,
225 4 : int nFileOffset /* = 0*/)
226 : {
227 : /*-----------------------------------------------------------------
228 : * Start with the default initialisation
229 : *----------------------------------------------------------------*/
230 4 : if ( TABRawBinBlock::InitNewBlock(fpSrc, nBlockSize, nFileOffset) != 0)
231 0 : return -1;
232 :
233 : /*-----------------------------------------------------------------
234 : * And then set default values for the block header.
235 : *----------------------------------------------------------------*/
236 4 : m_nNextToolBlock = 0;
237 :
238 4 : m_numDataBytes = 0;
239 :
240 4 : GotoByteInBlock(0x000);
241 :
242 4 : if (m_eAccess != TABRead)
243 : {
244 2 : WriteInt16(TABMAP_TOOL_BLOCK); // Block type code
245 2 : WriteInt16(0); // num. bytes used, excluding header
246 2 : WriteInt32(0); // Pointer to next tool block
247 : }
248 :
249 4 : if (CPLGetLastErrorNo() != 0)
250 0 : return -1;
251 :
252 4 : return 0;
253 : }
254 :
255 : /**********************************************************************
256 : * TABMAPToolBlock::SetNextToolBlock()
257 : *
258 : * Set the address (offset from beginning of file) of the drawing tool block
259 : * that follows the current one.
260 : **********************************************************************/
261 0 : void TABMAPToolBlock::SetNextToolBlock(GInt32 nNextToolBlockAddress)
262 : {
263 0 : m_nNextToolBlock = nNextToolBlockAddress;
264 0 : }
265 :
266 : /**********************************************************************
267 : * TABMAPToolBlock::SetMAPBlockManagerRef()
268 : *
269 : * Pass a reference to the block manager object for the file this
270 : * block belongs to. The block manager will be used by this object
271 : * when it needs to automatically allocate a new block.
272 : **********************************************************************/
273 2 : void TABMAPToolBlock::SetMAPBlockManagerRef(TABBinBlockManager *poBlockMgr)
274 : {
275 2 : m_poBlockManagerRef = poBlockMgr;
276 2 : };
277 :
278 :
279 : /**********************************************************************
280 : * TABMAPToolBlock::ReadBytes()
281 : *
282 : * Cover function for TABRawBinBlock::ReadBytes() that will automagically
283 : * load the next coordinate block in the chain before reading the
284 : * requested bytes if we are at the end of the current block and if
285 : * m_nNextToolBlock is a valid block.
286 : *
287 : * Then the control is passed to TABRawBinBlock::ReadBytes() to finish the
288 : * work:
289 : * Copy the number of bytes from the data block's internal buffer to
290 : * the user's buffer pointed by pabyDstBuf.
291 : *
292 : * Passing pabyDstBuf = NULL will only move the read pointer by the
293 : * specified number of bytes as if the copy had happened... but it
294 : * won't crash.
295 : *
296 : * Returns 0 if succesful or -1 if an error happened, in which case
297 : * CPLError() will have been called.
298 : **********************************************************************/
299 40 : int TABMAPToolBlock::ReadBytes(int numBytes, GByte *pabyDstBuf)
300 : {
301 : int nStatus;
302 :
303 40 : if (m_pabyBuf &&
304 : m_nCurPos >= (m_numDataBytes+MAP_TOOL_HEADER_SIZE) &&
305 : m_nNextToolBlock > 0)
306 : {
307 0 : if ( (nStatus=GotoByteInFile(m_nNextToolBlock)) != 0)
308 : {
309 : // Failed.... an error has already been reported.
310 0 : return nStatus;
311 : }
312 :
313 0 : GotoByteInBlock(MAP_TOOL_HEADER_SIZE); // Move pointer past header
314 0 : m_numBlocksInChain++;
315 : }
316 :
317 40 : return TABRawBinBlock::ReadBytes(numBytes, pabyDstBuf);
318 : }
319 :
320 : /**********************************************************************
321 : * TABMAPToolBlock::WriteBytes()
322 : *
323 : * Cover function for TABRawBinBlock::WriteBytes() that will automagically
324 : * CommitToFile() the current block and create a new one if we are at
325 : * the end of the current block.
326 : *
327 : * Then the control is passed to TABRawBinBlock::WriteBytes() to finish the
328 : * work.
329 : *
330 : * Passing pabySrcBuf = NULL will only move the write pointer by the
331 : * specified number of bytes as if the copy had happened... but it
332 : * won't crash.
333 : *
334 : * Returns 0 if succesful or -1 if an error happened, in which case
335 : * CPLError() will have been called.
336 : **********************************************************************/
337 48 : int TABMAPToolBlock::WriteBytes(int nBytesToWrite, GByte *pabySrcBuf)
338 : {
339 48 : if (m_eAccess == TABWrite && m_poBlockManagerRef &&
340 : (m_nBlockSize - m_nCurPos) < nBytesToWrite)
341 : {
342 0 : int nNewBlockOffset = m_poBlockManagerRef->AllocNewBlock();
343 0 : SetNextToolBlock(nNewBlockOffset);
344 :
345 0 : if (CommitToFile() != 0 ||
346 : InitNewBlock(m_fp, 512, nNewBlockOffset) != 0)
347 : {
348 : // An error message should have already been reported.
349 0 : return -1;
350 : }
351 :
352 0 : m_numBlocksInChain ++;
353 : }
354 :
355 48 : return TABRawBinBlock::WriteBytes(nBytesToWrite, pabySrcBuf);
356 : }
357 :
358 : /**********************************************************************
359 : * TABMAPToolBlock::CheckAvailableSpace()
360 : *
361 : * Check if an object of the specified type can fit in
362 : * current block. If it can't fit then force committing current block
363 : * and allocating a new one.
364 : *
365 : * Returns 0 if succesful or -1 if an error happened, in which case
366 : * CPLError() will have been called.
367 : **********************************************************************/
368 4 : int TABMAPToolBlock::CheckAvailableSpace(int nToolType)
369 : {
370 4 : int nBytesToWrite = 0;
371 :
372 4 : switch(nToolType)
373 : {
374 : case TABMAP_TOOL_PEN:
375 2 : nBytesToWrite = 11;
376 2 : break;
377 : case TABMAP_TOOL_BRUSH:
378 2 : nBytesToWrite = 13;
379 2 : break;
380 : case TABMAP_TOOL_FONT:
381 0 : nBytesToWrite = 37;
382 0 : break;
383 : case TABMAP_TOOL_SYMBOL:
384 0 : nBytesToWrite = 13;
385 0 : break;
386 : default:
387 0 : CPLAssert(FALSE);
388 : }
389 :
390 4 : if (GetNumUnusedBytes() < nBytesToWrite)
391 : {
392 0 : int nNewBlockOffset = m_poBlockManagerRef->AllocNewBlock();
393 0 : SetNextToolBlock(nNewBlockOffset);
394 :
395 0 : if (CommitToFile() != 0 ||
396 : InitNewBlock(m_fp, 512, nNewBlockOffset) != 0)
397 : {
398 : // An error message should have already been reported.
399 0 : return -1;
400 : }
401 :
402 0 : m_numBlocksInChain ++;
403 : }
404 :
405 4 : return 0;
406 : }
407 :
408 :
409 :
410 :
411 : /**********************************************************************
412 : * TABMAPToolBlock::Dump()
413 : *
414 : * Dump block contents... available only in DEBUG mode.
415 : **********************************************************************/
416 : #ifdef DEBUG
417 :
418 0 : void TABMAPToolBlock::Dump(FILE *fpOut /*=NULL*/)
419 : {
420 0 : if (fpOut == NULL)
421 0 : fpOut = stdout;
422 :
423 0 : fprintf(fpOut, "----- TABMAPToolBlock::Dump() -----\n");
424 0 : if (m_pabyBuf == NULL)
425 : {
426 0 : fprintf(fpOut, "Block has not been initialized yet.");
427 : }
428 : else
429 : {
430 : fprintf(fpOut,"Coordinate Block (type %d) at offset %d.\n",
431 0 : m_nBlockType, m_nFileOffset);
432 0 : fprintf(fpOut," m_numDataBytes = %d\n", m_numDataBytes);
433 0 : fprintf(fpOut," m_nNextToolBlock = %d\n", m_nNextToolBlock);
434 : }
435 :
436 0 : fflush(fpOut);
437 0 : }
438 :
439 : #endif // DEBUG
440 :
441 :
442 :
|