libdap Updated for version 3.21.0
libdap4 is an implementation of OPeNDAP's DAP protocol.
AISDatabaseParser.cc
1
2// -*- mode: c++; c-basic-offset:4 -*-
3
4// This file is part of libdap, A C++ implementation of the OPeNDAP Data
5// Access Protocol.
6
7// Copyright (c) 2003 OPeNDAP, Inc.
8// Author: James Gallagher <jgallagher@opendap.org>
9//
10// This library is free software; you can redistribute it and/or
11// modify it under the terms of the GNU Lesser General Public
12// License as published by the Free Software Foundation; either
13// version 2.1 of the License, or (at your option) any later version.
14//
15// This library is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18// Lesser General Public License for more details.
19//
20// You should have received a copy of the GNU Lesser General Public
21// License along with this library; if not, write to the Free Software
22// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23//
24// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
25
26#include "config.h"
27
28#include <cstring>
29#include <stdarg.h>
30
31#include "AISDatabaseParser.h"
32#include "util.h"
33#include "debug.h"
34
35using namespace std;
36
37namespace libdap {
38
39static const not_used char *states[] =
40 {
41 "START",
42 "FINISH",
43 "AIS",
44 "ENTRY",
45 "PRIMARY",
46 "ANCILLARY",
47 "UNKNOWN",
48 "ERROR"
49 };
50
57
61void
63{
64 state->state = PARSER_START;
65 state->unknown_depth = 0;
66 state->prev_state = PARSER_UNKNOWN;
67 state->error_msg = "";
68
69 DBG2(cerr << "Parser state: " << states[state->state] << endl);
70}
71
74void
76{
77 DBG2(cerr << "Ending state == " << states[state->state] << endl);
78
79 if (state->unknown_depth != 0) {
80 AISDatabaseParser::aisFatalError(state, "The document contained unbalanced tags.");
81
82 DBG(cerr << "unknown_depth != 0 (" << state->unknown_depth << ")"
83 << endl);
84 }
85}
86
95void
96AISDatabaseParser::aisStartElement(AISParserState *state, const char *name,
97 const char **attrs)
98{
99 switch (state->state) {
100 case PARSER_START:
101 if (strcmp(name, "ais") != 0) {
102 DBG(cerr << "Expecting ais. Got " << name << endl);
103 }
104 state->state = AIS;
105 break;
106
107 case PARSER_FINISH:
108 break;
109
110 case AIS:
111 if (strcmp(name, "entry") == 0) {
112 state->prev_state = state->state;
113 state->state = ENTRY;
114 }
115 else {
116 state->prev_state = state->state;
117 state->state = PARSER_UNKNOWN;
118 state->unknown_depth++;
119 }
120 break;
121
122 case ENTRY:
123 if (strcmp(name, "primary") == 0) {
124 state->prev_state = state->state;
125 state->state = PRIMARY;
126
127 if (attrs) {
128 if (strcmp(attrs[0], "url") == 0) {
129 state->regexp = false;
130 state->primary = attrs[1];
131 }
132 else if (strcmp(attrs[0], "regexp") == 0) {
133 state->regexp = true;
134 state->primary = attrs[1];
135 }
136 }
137 else {
138 AISDatabaseParser::aisFatalError(state, "Required attribute 'url' or 'regexp' missing from element 'primary'.");
139 break;
140 }
141 }
142 else if (strcmp(name, "ancillary") == 0) {
143 state->prev_state = state->state;
144 state->state = ANCILLARY;
145
146 string url = ""; // set defaults, MUST have url
147 string rule = "overwrite";
148 for (int i = 0; attrs && attrs[i] != 0; i = i + 2) {
149 if (strcmp(attrs[i], "url") == 0)
150 url = attrs[i+1];
151 else if (strcmp(attrs[i], "rule") == 0)
152 rule = attrs[i+1];
153 }
154
155 // If this parser validated the XML, these tests would be
156 // unnecessary.
157 if (url == "") {
158 AISDatabaseParser::aisFatalError(state, "Required attribute 'url' missing from element 'ancillary'.");
159 break;
160 }
161
162 if (rule != "overwrite" && rule != "replace" && rule != "fallback") {
163 string msg = string("Optional attribute 'rule' in element 'ancillary' has a bad value: ") + rule + "\nIt should be one of 'overwrite', 'replace' or 'fallback'.";
164 AISDatabaseParser::aisFatalError(state, msg.c_str());
165 break;
166 }
167
168 Resource r(url, rule);
169 state->rv.push_back(r);
170 }
171 else {
172 state->prev_state = state->state;
173 state->state = PARSER_UNKNOWN;
174 state->unknown_depth++;
175 }
176 break;
177
178 case PRIMARY:
179 break;
180
181 case ANCILLARY:
182 break;
183
184 case PARSER_UNKNOWN:
185 state->unknown_depth++;
186 break;
187
188 case PARSER_ERROR:
189 break;
190 }
191
192 DBG2(cerr << "Start element " << name << " (state "
193 << states[state->state] << ")" << endl);
194}
195
201// Although not used in the method itself, name is used in the DBG2
202// statement, so we need the parameter name. - pcw 07/08/08
203void
204AISDatabaseParser::aisEndElement(AISParserState *state, const char */*name*/)
205{
206 DBG2(cerr << "End element: state " << states[state->state] << endl);
207
208 switch (state->state) {
209 case AIS:
210 state->prev_state = state->state;
211 state->state = PARSER_FINISH;
212 break;
213
214 case ENTRY:
215 state->prev_state = state->state;
216 state->state = AIS;
217
218 // record 'primary' and 'rv'
219 if (state->regexp)
220 state->ais->add_regexp_resource(state->primary, state->rv);
221 else
222 state->ais->add_url_resource(state->primary, state->rv);
223
224 // empty rv for the next set of ancillary resources.
225 state->rv.erase(state->rv.begin(), state->rv.end());
226 break;
227
228 case PRIMARY:
229 state->prev_state = state->state;
230 state->state = ENTRY;
231 break;
232
233 case ANCILLARY:
234 state->prev_state = state->state;
235 state->state = ENTRY;
236 break;
237
238 case PARSER_UNKNOWN:
239 // Leave the state and prev_state alone.
240 state->unknown_depth--;
241 break;
242
243 case PARSER_ERROR:
244 break;
245
246 default:
247 break;
248 }
249}
250
254xmlEntityPtr
255AISDatabaseParser::aisGetEntity(AISParserState *, const xmlChar *name)
256{
257 return xmlGetPredefinedEntity(name);
258}
259
264void
265AISDatabaseParser::aisWarning(AISParserState *state, const char *msg, ...)
266{
267 va_list args;
268
269 state->state = PARSER_ERROR;
270
271 va_start(args, msg);
272 char str[1024];
273 vsnprintf(str, 1024, msg, args);
274 va_end(args);
275
276#ifdef LIBXML2_6_16
277 // Defined if libxml2 >= 2.6.16
278 int line = xmlSAX2GetLineNumber(state->ctxt);
279#else
280 int line = getLineNumber(state->ctxt);
281#endif
282 state->error_msg += "At line: " + long_to_string(line) + ": ";
283 state->error_msg += string(str) + string("\n");
284}
285
290void
291AISDatabaseParser::aisError(AISParserState *state, const char *msg, ...)
292{
293 va_list args;
294
295 state->state = PARSER_ERROR;
296
297 va_start(args, msg);
298 char str[1024];
299 vsnprintf(str, 1024, msg, args);
300 va_end(args);
301
302#ifdef LIBXML2_6_16
303 // Defined if libxml2 >= 2.6.16
304 int line = xmlSAX2GetLineNumber(state->ctxt);
305#else
306 int line = getLineNumber(state->ctxt);
307#endif
308 state->error_msg += "At line: " + long_to_string(line) + ": ";
309 state->error_msg += string(str) + string("\n");
310}
311
315void
316AISDatabaseParser::aisFatalError(AISParserState *state, const char *msg, ...)
317{
318 va_list args;
319
320 state->state = PARSER_ERROR;
321
322 va_start(args, msg);
323 char str[1024];
324 vsnprintf(str, 1024, msg, args);
325 va_end(args);
326
327#ifdef LIBXML2_6_16
328 // Defined if libxml2 >= 2.6.16
329 int line = xmlSAX2GetLineNumber(state->ctxt);
330#else
331 int line = getLineNumber(state->ctxt);
332#endif
333 state->error_msg += "At line: " + long_to_string(line) + ": ";
334 state->error_msg += string(str) + string("\n");
335}
336
338
341static xmlSAXHandler aisSAXParser =
342 {
343 0, // internalSubset
344 0, // isStandalone
345 0, // hasInternalSubset
346 0, // hasExternalSubset
347 0, // resolveEntity
348 (getEntitySAXFunc)AISDatabaseParser::aisGetEntity, // getEntity
349 0, // entityDecl
350 0, // notationDecl
351 0, // attributeDecl
352 0, // elementDecl
353 0, // unparsedEntityDecl
354 0, // setDocumentLocator
355 (startDocumentSAXFunc)AISDatabaseParser::aisStartDocument, // startDocument
356 (endDocumentSAXFunc)AISDatabaseParser::aisEndDocument, // endDocument
357 (startElementSAXFunc)AISDatabaseParser::aisStartElement, // startElement
358 (endElementSAXFunc)AISDatabaseParser::aisEndElement, // endElement
359 0, // reference
360 0, // (charactersSAXFunc)gladeCharacters, characters
361 0, // ignorableWhitespace
362 0, // processingInstruction
363 0, // (commentSAXFunc)gladeComment, comment
364 (warningSAXFunc)AISDatabaseParser::aisWarning, // warning
365 (errorSAXFunc)AISDatabaseParser::aisError, // error
366 (fatalErrorSAXFunc)AISDatabaseParser::aisFatalError, // fatalError
367#ifdef LIBXML2_5_10
368 0, // getParameterEntity
369 0, // cdataBlock
370 0, // externalSubset
371 0, // initialized
372#endif
373#ifdef LIBXML2_6_16
374 0, // _private
375 0, // endElementNs
376 0, // serror
377 0 // startElementNs
378#endif
379 };
380
387void
388AISDatabaseParser::intern(const string &database, AISResources *ais)
389{
390 xmlParserCtxtPtr ctxt;
391 AISParserState state;
392
393 ctxt = xmlCreateFileParserCtxt(database.c_str());
394 if (!ctxt)
395 return;
396
397 state.ais = ais; // dump values here
398 state.ctxt = ctxt; // need ctxt for error messages
399
400 ctxt->sax = &aisSAXParser;
401 ctxt->userData = &state;
402 ctxt->validate = true;
403
404 xmlParseDocument(ctxt);
405
406 // use getLineNumber and getColumnNumber to make the error messages better.
407 if (!ctxt->wellFormed) {
408 ctxt->sax = NULL;
409 xmlFreeParserCtxt(ctxt);
410 throw AISDatabaseReadFailed(string("\nThe database is not a well formed XML document.\n") + state.error_msg);
411 }
412
413 if (!ctxt->valid) {
414 ctxt->sax = NULL;
415 xmlFreeParserCtxt(ctxt);
416 throw AISDatabaseReadFailed(string("\nThe database is not a valid document.\n") + state.error_msg);
417 }
418
419 if (state.state == PARSER_ERROR) {
420 ctxt->sax = NULL;
421 xmlFreeParserCtxt(ctxt);
422 throw AISDatabaseReadFailed(string("\nError parsing AIS resources.\n") + state.error_msg);
423 }
424
425 ctxt->sax = NULL;
426 xmlFreeParserCtxt(ctxt);
427}
428
429} // namespace libdap
static void aisWarning(AISParserState *state, const char *msg,...)
static void aisStartElement(AISParserState *state, const char *name, const char **attrs)
static xmlEntityPtr aisGetEntity(AISParserState *state, const xmlChar *name)
static void aisStartDocument(AISParserState *state)
void intern(const string &database, AISResources *ais)
static void aisEndElement(AISParserState *state, const char *name)
static void aisEndDocument(AISParserState *state)
static void aisFatalError(AISParserState *state, const char *msg,...)
static void aisError(AISParserState *state, const char *msg,...)
Manage AIS resources.
Associate a rule with an ancillary resource.
Definition Resource.h:51
top level DAP object to house generic methods
Definition AISConnect.cc:30