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