libdap Updated for version 3.21.1
libdap4 is an implementation of OPeNDAP's DAP protocol.
AISDatabaseParser.cc
Go to the documentation of this file.
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 "debug.h"
33#include "util.h"
34
35using namespace std;
36
37namespace libdap {
38
39static const not_used char *states[] = {"START", "FINISH", "AIS", "ENTRY", "PRIMARY", "ANCILLARY", "UNKNOWN", "ERROR"};
40
47
51void AISDatabaseParser::aisStartDocument(AISParserState *state) {
52 state->state = PARSER_START;
53 state->unknown_depth = 0;
54 state->prev_state = PARSER_UNKNOWN;
55 state->error_msg = "";
56
57 DBG2(cerr << "Parser state: " << states[state->state] << endl);
58}
59
62void AISDatabaseParser::aisEndDocument(AISParserState *state) {
63 DBG2(cerr << "Ending state == " << states[state->state] << endl);
64
65 if (state->unknown_depth != 0) {
66 AISDatabaseParser::aisFatalError(state, "The document contained unbalanced tags.");
67
68 DBG(cerr << "unknown_depth != 0 (" << state->unknown_depth << ")" << endl);
69 }
70}
71
80void AISDatabaseParser::aisStartElement(AISParserState *state, const char *name, const char **attrs) {
81 switch (state->state) {
82 case PARSER_START:
83 if (strcmp(name, "ais") != 0) {
84 DBG(cerr << "Expecting ais. Got " << name << endl);
85 }
86 state->state = AIS;
87 break;
88
89 case PARSER_FINISH:
90 break;
91
92 case AIS:
93 if (strcmp(name, "entry") == 0) {
94 state->prev_state = state->state;
95 state->state = ENTRY;
96 } else {
97 state->prev_state = state->state;
98 state->state = PARSER_UNKNOWN;
99 state->unknown_depth++;
100 }
101 break;
102
103 case ENTRY:
104 if (strcmp(name, "primary") == 0) {
105 state->prev_state = state->state;
106 state->state = PRIMARY;
107
108 if (attrs) {
109 if (strcmp(attrs[0], "url") == 0) {
110 state->regexp = false;
111 state->primary = attrs[1];
112 } else if (strcmp(attrs[0], "regexp") == 0) {
113 state->regexp = true;
114 state->primary = attrs[1];
115 }
116 } else {
118 state, "Required attribute 'url' or 'regexp' missing from element 'primary'.");
119 break;
120 }
121 } else if (strcmp(name, "ancillary") == 0) {
122 state->prev_state = state->state;
123 state->state = ANCILLARY;
124
125 string url = ""; // set defaults, MUST have url
126 string rule = "overwrite";
127 for (int i = 0; attrs && attrs[i] != 0; i = i + 2) {
128 if (strcmp(attrs[i], "url") == 0)
129 url = attrs[i + 1];
130 else if (strcmp(attrs[i], "rule") == 0)
131 rule = attrs[i + 1];
132 }
133
134 // If this parser validated the XML, these tests would be
135 // unnecessary.
136 if (url == "") {
137 AISDatabaseParser::aisFatalError(state, "Required attribute 'url' missing from element 'ancillary'.");
138 break;
139 }
140
141 if (rule != "overwrite" && rule != "replace" && rule != "fallback") {
142 string msg = string("Optional attribute 'rule' in element 'ancillary' has a bad value: ") + rule +
143 "\nIt should be one of 'overwrite', 'replace' or 'fallback'.";
144 AISDatabaseParser::aisFatalError(state, msg.c_str());
145 break;
146 }
147
148 Resource r(url, rule);
149 state->rv.push_back(r);
150 } else {
151 state->prev_state = state->state;
152 state->state = PARSER_UNKNOWN;
153 state->unknown_depth++;
154 }
155 break;
156
157 case PRIMARY:
158 break;
159
160 case ANCILLARY:
161 break;
162
163 case PARSER_UNKNOWN:
164 state->unknown_depth++;
165 break;
166
167 case PARSER_ERROR:
168 break;
169 }
170
171 DBG2(cerr << "Start element " << name << " (state " << states[state->state] << ")" << endl);
172}
173
179// Although not used in the method itself, name is used in the DBG2
180// statement, so we need the parameter name. - pcw 07/08/08
181void AISDatabaseParser::aisEndElement(AISParserState *state, const char * /*name*/) {
182 DBG2(cerr << "End element: state " << states[state->state] << endl);
183
184 switch (state->state) {
185 case AIS:
186 state->prev_state = state->state;
187 state->state = PARSER_FINISH;
188 break;
189
190 case ENTRY:
191 state->prev_state = state->state;
192 state->state = AIS;
193
194 // record 'primary' and 'rv'
195 if (state->regexp)
196 state->ais->add_regexp_resource(state->primary, state->rv);
197 else
198 state->ais->add_url_resource(state->primary, state->rv);
199
200 // empty rv for the next set of ancillary resources.
201 state->rv.erase(state->rv.begin(), state->rv.end());
202 break;
203
204 case PRIMARY:
205 state->prev_state = state->state;
206 state->state = ENTRY;
207 break;
208
209 case ANCILLARY:
210 state->prev_state = state->state;
211 state->state = ENTRY;
212 break;
213
214 case PARSER_UNKNOWN:
215 // Leave the state and prev_state alone.
216 state->unknown_depth--;
217 break;
218
219 case PARSER_ERROR:
220 break;
221
222 default:
223 break;
224 }
225}
226
230xmlEntityPtr AISDatabaseParser::aisGetEntity(AISParserState *, const xmlChar *name) {
231 return xmlGetPredefinedEntity(name);
232}
233
238void AISDatabaseParser::aisWarning(AISParserState *state, const char *msg, ...) {
239 va_list args;
240
241 state->state = PARSER_ERROR;
242
243 va_start(args, msg);
244 char str[1024];
245 vsnprintf(str, 1024, msg, args);
246 va_end(args);
247
248#ifdef LIBXML2_6_16
249 // Defined if libxml2 >= 2.6.16
250 int line = xmlSAX2GetLineNumber(state->ctxt);
251#else
252 int line = getLineNumber(state->ctxt);
253#endif
254 state->error_msg += "At line: " + long_to_string(line) + ": ";
255 state->error_msg += string(str) + string("\n");
256}
257
262void AISDatabaseParser::aisError(AISParserState *state, const char *msg, ...) {
263 va_list args;
264
265 state->state = PARSER_ERROR;
266
267 va_start(args, msg);
268 char str[1024];
269 vsnprintf(str, 1024, msg, args);
270 va_end(args);
271
272#ifdef LIBXML2_6_16
273 // Defined if libxml2 >= 2.6.16
274 int line = xmlSAX2GetLineNumber(state->ctxt);
275#else
276 int line = getLineNumber(state->ctxt);
277#endif
278 state->error_msg += "At line: " + long_to_string(line) + ": ";
279 state->error_msg += string(str) + string("\n");
280}
281
285void AISDatabaseParser::aisFatalError(AISParserState *state, const char *msg, ...) {
286 va_list args;
287
288 state->state = PARSER_ERROR;
289
290 va_start(args, msg);
291 char str[1024];
292 vsnprintf(str, 1024, msg, args);
293 va_end(args);
294
295#ifdef LIBXML2_6_16
296 // Defined if libxml2 >= 2.6.16
297 int line = xmlSAX2GetLineNumber(state->ctxt);
298#else
299 int line = getLineNumber(state->ctxt);
300#endif
301 state->error_msg += "At line: " + long_to_string(line) + ": ";
302 state->error_msg += string(str) + string("\n");
303}
304
306
309static xmlSAXHandler aisSAXParser = {
310 0, // internalSubset
311 0, // isStandalone
312 0, // hasInternalSubset
313 0, // hasExternalSubset
314 0, // resolveEntity
315 (getEntitySAXFunc)AISDatabaseParser::aisGetEntity, // getEntity
316 0, // entityDecl
317 0, // notationDecl
318 0, // attributeDecl
319 0, // elementDecl
320 0, // unparsedEntityDecl
321 0, // setDocumentLocator
322 (startDocumentSAXFunc)AISDatabaseParser::aisStartDocument, // startDocument
323 (endDocumentSAXFunc)AISDatabaseParser::aisEndDocument, // endDocument
324 (startElementSAXFunc)AISDatabaseParser::aisStartElement, // startElement
325 (endElementSAXFunc)AISDatabaseParser::aisEndElement, // endElement
326 0, // reference
327 0, // (charactersSAXFunc)gladeCharacters, characters
328 0, // ignorableWhitespace
329 0, // processingInstruction
330 0, // (commentSAXFunc)gladeComment, comment
331 (warningSAXFunc)AISDatabaseParser::aisWarning, // warning
332 (errorSAXFunc)AISDatabaseParser::aisError, // error
333 (fatalErrorSAXFunc)AISDatabaseParser::aisFatalError, // fatalError
334#ifdef LIBXML2_5_10
335 0, // getParameterEntity
336 0, // cdataBlock
337 0, // externalSubset
338 0, // initialized
339#endif
340#ifdef LIBXML2_6_16
341 0, // _private
342 0, // endElementNs
343 0, // serror
344 0 // startElementNs
345#endif
346};
347
354void AISDatabaseParser::intern(const string &database, AISResources *ais) {
355 xmlParserCtxtPtr ctxt;
356 AISParserState state;
357
358 ctxt = xmlCreateFileParserCtxt(database.c_str());
359 if (!ctxt)
360 return;
361
362 state.ais = ais; // dump values here
363 state.ctxt = ctxt; // need ctxt for error messages
364
365 ctxt->sax = &aisSAXParser;
366 ctxt->userData = &state;
367 ctxt->validate = true;
368
369 xmlParseDocument(ctxt);
370
371 // use getLineNumber and getColumnNumber to make the error messages better.
372 if (!ctxt->wellFormed) {
373 ctxt->sax = NULL;
374 xmlFreeParserCtxt(ctxt);
375 throw AISDatabaseReadFailed(string("\nThe database is not a well formed XML document.\n") + state.error_msg);
376 }
377
378 if (!ctxt->valid) {
379 ctxt->sax = NULL;
380 xmlFreeParserCtxt(ctxt);
381 throw AISDatabaseReadFailed(string("\nThe database is not a valid document.\n") + state.error_msg);
382 }
383
384 if (state.state == PARSER_ERROR) {
385 ctxt->sax = NULL;
386 xmlFreeParserCtxt(ctxt);
387 throw AISDatabaseReadFailed(string("\nError parsing AIS resources.\n") + state.error_msg);
388 }
389
390 ctxt->sax = NULL;
391 xmlFreeParserCtxt(ctxt);
392}
393
394} // 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.
virtual void add_regexp_resource(const string &regexp, const Resource &ancillary)
virtual void add_url_resource(const string &url, const Resource &ancillary)
Associate a rule with an ancillary resource.
Definition Resource.h:49
#define not_used
Definition config_dap.h:17
#define DBG(x)
Definition debug.h:58
#define DBG2(x)
Definition debug.h:74
top level DAP object to house generic methods
Definition AISConnect.cc:30
string long_to_string(long val, int base)
Definition util.cc:946