Line data Source code
1 : /******************************************************************************
2 : *
3 : * Purpose: Implementation of the MetadataSegment class.
4 : *
5 : * This class is used to manage access to the SYS METADATA segment. This
6 : * segment holds all the metadata for objects in the PCIDSK file.
7 : *
8 : * This class is closely partnered with the MetadataSet class.
9 : *
10 : ******************************************************************************
11 : * Copyright (c) 2009
12 : * PCI Geomatics, 90 Allstate Parkway, Markham, Ontario, Canada.
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
25 : * OR 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 : #include "pcidsk_exception.h"
34 : #include "pcidsk_file.h"
35 : #include "segment/metadatasegment.h"
36 : #include <cassert>
37 : #include <cstring>
38 : #include <cstdio>
39 : #include <map>
40 :
41 : using namespace PCIDSK;
42 :
43 : /************************************************************************/
44 : /* MetadataSegment() */
45 : /************************************************************************/
46 :
47 97 : MetadataSegment::MetadataSegment( PCIDSKFile *fileIn, int segmentIn,
48 97 : const char *segment_pointer )
49 97 : : CPCIDSKSegment( fileIn, segmentIn, segment_pointer )
50 :
51 : {
52 97 : loaded = false;
53 97 : }
54 :
55 : /************************************************************************/
56 : /* ~MetadataSegment() */
57 : /************************************************************************/
58 :
59 194 : MetadataSegment::~MetadataSegment()
60 :
61 : {
62 : try
63 : {
64 97 : Synchronize();
65 : }
66 0 : catch( const PCIDSKException& ex )
67 : {
68 0 : fprintf( stderr, /*ok*/
69 : "Exception in MetadataSegment destructor: %s\n",
70 0 : ex.what() );
71 : }
72 0 : catch( ... )
73 : {
74 0 : fprintf( stderr, /*ok*/
75 : "PCIDSK SDK Failure in MetadataSegment destructor, "
76 : "unexpected exception.\n" );
77 : }
78 194 : }
79 :
80 : /************************************************************************/
81 : /* Synchronize() */
82 : /************************************************************************/
83 :
84 176 : void MetadataSegment::Synchronize()
85 : {
86 211 : if( loaded && !update_list.empty() &&
87 35 : this->file->GetUpdatable())
88 35 : Save();
89 176 : }
90 :
91 : /************************************************************************/
92 : /* Load() */
93 : /************************************************************************/
94 :
95 210 : void MetadataSegment::Load()
96 :
97 : {
98 210 : if( loaded )
99 113 : return;
100 :
101 : // TODO: this should likely be protected by a mutex.
102 :
103 : /* -------------------------------------------------------------------- */
104 : /* Load the segment contents into a buffer. */
105 : /* -------------------------------------------------------------------- */
106 :
107 : // data_size < 1024 will throw an exception in SetSize()
108 97 : seg_data.SetSize( data_size < 1024 ? -1 : (int) (data_size - 1024) );
109 :
110 97 : ReadFromFile( seg_data.buffer, 0, data_size - 1024 );
111 :
112 97 : loaded = true;
113 : }
114 :
115 : /************************************************************************/
116 : /* FetchGroupMetadata() */
117 : /************************************************************************/
118 :
119 139 : void MetadataSegment::FetchGroupMetadata( const char *group, int id,
120 : std::map<std::string,std::string> &md_set)
121 :
122 : {
123 : /* -------------------------------------------------------------------- */
124 : /* Load the metadata segment if not already loaded. */
125 : /* -------------------------------------------------------------------- */
126 139 : Load();
127 :
128 : /* -------------------------------------------------------------------- */
129 : /* Establish the key prefix we are searching for. */
130 : /* -------------------------------------------------------------------- */
131 : char key_prefix[200];
132 : size_t prefix_len;
133 :
134 139 : snprintf( key_prefix, sizeof(key_prefix), "METADATA_%s_%d_", group, id );
135 139 : prefix_len = std::strlen(key_prefix);
136 :
137 : /* -------------------------------------------------------------------- */
138 : /* Process all the metadata entries in this segment, searching */
139 : /* for those that match our prefix. */
140 : /* -------------------------------------------------------------------- */
141 : const char *pszNext;
142 :
143 485 : for( pszNext = (const char *) seg_data.buffer; *pszNext != '\0'; )
144 : {
145 : /* -------------------------------------------------------------------- */
146 : /* Identify the end of this line, and the split character (:). */
147 : /* -------------------------------------------------------------------- */
148 346 : int i_split = -1, i;
149 :
150 12054 : for( i=0;
151 12054 : pszNext[i] != 10 && pszNext[i] != 12 && pszNext[i] != 0;
152 : i++)
153 : {
154 11708 : if( i_split == -1 && pszNext[i] == ':' )
155 346 : i_split = i;
156 : }
157 :
158 346 : if( pszNext[i] == '\0' )
159 0 : break;
160 :
161 : /* -------------------------------------------------------------------- */
162 : /* If this matches our prefix, capture the key and value. */
163 : /* -------------------------------------------------------------------- */
164 346 : if( i_split != -1 && std::strncmp(pszNext,key_prefix,prefix_len) == 0 )
165 : {
166 212 : std::string key, value;
167 :
168 106 : key.assign( pszNext+prefix_len, i_split-prefix_len );
169 :
170 106 : if( pszNext[i_split+1] == ' ' )
171 106 : value.assign( pszNext+i_split+2, i-i_split-2 );
172 : else
173 0 : value.assign( pszNext+i_split+1, i-i_split-1 );
174 :
175 106 : md_set[key] = std::move(value);
176 : }
177 :
178 : /* -------------------------------------------------------------------- */
179 : /* Advance to start of next line. */
180 : /* -------------------------------------------------------------------- */
181 346 : pszNext = pszNext + i;
182 692 : while( *pszNext == 10 || *pszNext == 12 )
183 346 : pszNext++;
184 : }
185 139 : }
186 :
187 : /************************************************************************/
188 : /* SetGroupMetadataValue() */
189 : /************************************************************************/
190 :
191 71 : void MetadataSegment::SetGroupMetadataValue( const char *group, int id,
192 : const std::string& key, const std::string& value )
193 :
194 : {
195 71 : Load();
196 :
197 : char key_prefix[200];
198 :
199 71 : snprintf( key_prefix, sizeof(key_prefix), "METADATA_%s_%d_", group, id );
200 :
201 142 : std::string full_key;
202 :
203 71 : full_key = key_prefix;
204 71 : full_key += key;
205 :
206 71 : update_list[full_key] = value;
207 71 : }
208 :
209 : /************************************************************************/
210 : /* Save() */
211 : /* */
212 : /* When saving we first need to merge in any updates. We put */
213 : /* this off since scanning and updating the metadata doc could */
214 : /* be expensive if done for each item. */
215 : /************************************************************************/
216 :
217 35 : void MetadataSegment::Save()
218 :
219 : {
220 70 : std::string new_data;
221 :
222 : /* -------------------------------------------------------------------- */
223 : /* Process all the metadata entries in this segment, searching */
224 : /* for those that match our prefix. */
225 : /* -------------------------------------------------------------------- */
226 : const char *pszNext;
227 :
228 51 : for( pszNext = (const char *) seg_data.buffer; *pszNext != '\0'; )
229 : {
230 : /* -------------------------------------------------------------------- */
231 : /* Identify the end of this line, and the split character (:). */
232 : /* -------------------------------------------------------------------- */
233 16 : int i_split = -1, i;
234 :
235 408 : for( i=0;
236 408 : pszNext[i] != 10 && pszNext[i] != 12 && pszNext[i] != 0;
237 : i++)
238 : {
239 392 : if( i_split == -1 && pszNext[i] == ':' )
240 16 : i_split = i;
241 : }
242 :
243 16 : if( pszNext[i] == '\0' )
244 0 : break;
245 : /* -------------------------------------------------------------------- */
246 : /* If we have a new value for this key, do not copy over the */
247 : /* old value. Otherwise append the old value to our new image. */
248 : /* -------------------------------------------------------------------- */
249 16 : if (i_split != -1)
250 : {
251 32 : std::string full_key;
252 :
253 16 : full_key.assign( pszNext, i_split );
254 :
255 16 : if( update_list.count(full_key) == 1 )
256 : /* do not transfer - we will append later */;
257 : else
258 13 : new_data.append( pszNext, i+1 );
259 : }
260 :
261 : /* -------------------------------------------------------------------- */
262 : /* Advance to start of next line. */
263 : /* -------------------------------------------------------------------- */
264 16 : pszNext = pszNext + i;
265 32 : while( *pszNext == 10 || *pszNext == 12 )
266 16 : pszNext++;
267 : }
268 :
269 : /* -------------------------------------------------------------------- */
270 : /* Append all the update items with non-empty values. */
271 : /* -------------------------------------------------------------------- */
272 35 : std::map<std::string,std::string>::iterator it;
273 :
274 104 : for( it = update_list.begin(); it != update_list.end(); ++it )
275 : {
276 69 : if( it->second.empty() )
277 1 : continue;
278 :
279 136 : std::string line;
280 :
281 68 : line = it->first;
282 68 : line += ": ";
283 68 : line += it->second;
284 68 : line += "\n";
285 :
286 68 : new_data += line;
287 : }
288 :
289 35 : update_list.clear();
290 :
291 : /* -------------------------------------------------------------------- */
292 : /* Move the new value into our buffer, and write to disk. */
293 : /* -------------------------------------------------------------------- */
294 35 : if( new_data.size() % 512 != 0 ) // zero fill the last block.
295 : {
296 35 : new_data.resize( new_data.size() + (512 - (new_data.size() % 512)),
297 : '\0' );
298 : }
299 :
300 35 : seg_data.SetSize( static_cast<int>(new_data.size()) );
301 35 : std::memcpy( seg_data.buffer, new_data.c_str(), new_data.size() );
302 :
303 35 : WriteToFile( seg_data.buffer, 0, seg_data.buffer_size );
304 35 : }
|