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