libdap  Updated for version 3.20.6
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 #if 0
32 // Removed for VC 2008 compatibility. jhrg 4/23/08
33 #ifdef WIN32
34 #define vsnprintf _vsnprintf
35 #endif
36 #endif
37 
38 #include "AISDatabaseParser.h"
39 #include "util.h"
40 #include "debug.h"
41 
42 using namespace std;
43 
44 namespace libdap {
45 
46 static const not_used char *states[] =
47  {
48  "START",
49  "FINISH",
50  "AIS",
51  "ENTRY",
52  "PRIMARY",
53  "ANCILLARY",
54  "UNKNOWN",
55  "ERROR"
56  };
57 
64 
68 void
69 AISDatabaseParser::aisStartDocument(AISParserState *state)
70 {
71  state->state = PARSER_START;
72  state->unknown_depth = 0;
73  state->prev_state = PARSER_UNKNOWN;
74  state->error_msg = "";
75 
76  DBG2(cerr << "Parser state: " << states[state->state] << endl);
77 }
78 
81 void
82 AISDatabaseParser::aisEndDocument(AISParserState *state)
83 {
84  DBG2(cerr << "Ending state == " << states[state->state] << endl);
85 
86  if (state->unknown_depth != 0) {
87  AISDatabaseParser::aisFatalError(state, "The document contained unbalanced tags.");
88 
89  DBG(cerr << "unknown_depth != 0 (" << state->unknown_depth << ")"
90  << endl);
91  }
92 }
93 
102 void
103 AISDatabaseParser::aisStartElement(AISParserState *state, const char *name,
104  const char **attrs)
105 {
106  switch (state->state) {
107  case PARSER_START:
108  if (strcmp(name, "ais") != 0) {
109  DBG(cerr << "Expecting ais. Got " << name << endl);
110  }
111  state->state = AIS;
112  break;
113 
114  case PARSER_FINISH:
115  break;
116 
117  case AIS:
118  if (strcmp(name, "entry") == 0) {
119  state->prev_state = state->state;
120  state->state = ENTRY;
121  }
122  else {
123  state->prev_state = state->state;
124  state->state = PARSER_UNKNOWN;
125  state->unknown_depth++;
126  }
127  break;
128 
129  case ENTRY:
130  if (strcmp(name, "primary") == 0) {
131  state->prev_state = state->state;
132  state->state = PRIMARY;
133 
134  if (attrs) {
135  if (strcmp(attrs[0], "url") == 0) {
136  state->regexp = false;
137  state->primary = attrs[1];
138  }
139  else if (strcmp(attrs[0], "regexp") == 0) {
140  state->regexp = true;
141  state->primary = attrs[1];
142  }
143  }
144  else {
145  AISDatabaseParser::aisFatalError(state, "Required attribute 'url' or 'regexp' missing from element 'primary'.");
146  break;
147  }
148  }
149  else if (strcmp(name, "ancillary") == 0) {
150  state->prev_state = state->state;
151  state->state = ANCILLARY;
152 
153  string url = ""; // set defaults, MUST have url
154  string rule = "overwrite";
155  for (int i = 0; attrs && attrs[i] != 0; i = i + 2) {
156  if (strcmp(attrs[i], "url") == 0)
157  url = attrs[i+1];
158  else if (strcmp(attrs[i], "rule") == 0)
159  rule = attrs[i+1];
160  }
161 
162  // If this parser validated the XML, these tests would be
163  // unnecessary.
164  if (url == "") {
165  AISDatabaseParser::aisFatalError(state, "Required attribute 'url' missing from element 'ancillary'.");
166  break;
167  }
168 
169  if (rule != "overwrite" && rule != "replace" && rule != "fallback") {
170  string msg = string("Optional attribute 'rule' in element 'ancillary' has a bad value: ") + rule + "\nIt should be one of 'overwrite', 'replace' or 'fallback'.";
171  AISDatabaseParser::aisFatalError(state, msg.c_str());
172  break;
173  }
174 
175  Resource r(url, rule);
176  state->rv.push_back(r);
177  }
178  else {
179  state->prev_state = state->state;
180  state->state = PARSER_UNKNOWN;
181  state->unknown_depth++;
182  }
183  break;
184 
185  case PRIMARY:
186  break;
187 
188  case ANCILLARY:
189  break;
190 
191  case PARSER_UNKNOWN:
192  state->unknown_depth++;
193  break;
194 
195  case PARSER_ERROR:
196  break;
197  }
198 
199  DBG2(cerr << "Start element " << name << " (state "
200  << states[state->state] << ")" << endl);
201 }
202 
208 // Although not used in the method itself, name is used in the DBG2
209 // statement, so we need the parameter name. - pcw 07/08/08
210 void
211 AISDatabaseParser::aisEndElement(AISParserState *state, const char */*name*/)
212 {
213  DBG2(cerr << "End element: state " << states[state->state] << endl);
214 
215  switch (state->state) {
216  case AIS:
217  state->prev_state = state->state;
218  state->state = PARSER_FINISH;
219  break;
220 
221  case ENTRY:
222  state->prev_state = state->state;
223  state->state = AIS;
224 
225  // record 'primary' and 'rv'
226  if (state->regexp)
227  state->ais->add_regexp_resource(state->primary, state->rv);
228  else
229  state->ais->add_url_resource(state->primary, state->rv);
230 
231  // empty rv for the next set of ancillary resources.
232  state->rv.erase(state->rv.begin(), state->rv.end());
233  break;
234 
235  case PRIMARY:
236  state->prev_state = state->state;
237  state->state = ENTRY;
238  break;
239 
240  case ANCILLARY:
241  state->prev_state = state->state;
242  state->state = ENTRY;
243  break;
244 
245  case PARSER_UNKNOWN:
246  // Leave the state and prev_state alone.
247  state->unknown_depth--;
248  break;
249 
250  case PARSER_ERROR:
251  break;
252 
253  default:
254  break;
255  }
256 }
257 
261 xmlEntityPtr
262 AISDatabaseParser::aisGetEntity(AISParserState *, const xmlChar *name)
263 {
264  return xmlGetPredefinedEntity(name);
265 }
266 
271 void
272 AISDatabaseParser::aisWarning(AISParserState *state, const char *msg, ...)
273 {
274  va_list args;
275 
276  state->state = PARSER_ERROR;
277 
278  va_start(args, msg);
279  char str[1024];
280  vsnprintf(str, 1024, msg, args);
281  va_end(args);
282 
283 #ifdef LIBXML2_6_16
284  // Defined if libxml2 >= 2.6.16
285  int line = xmlSAX2GetLineNumber(state->ctxt);
286 #else
287  int line = getLineNumber(state->ctxt);
288 #endif
289  state->error_msg += "At line: " + long_to_string(line) + ": ";
290  state->error_msg += string(str) + string("\n");
291 }
292 
297 void
298 AISDatabaseParser::aisError(AISParserState *state, const char *msg, ...)
299 {
300  va_list args;
301 
302  state->state = PARSER_ERROR;
303 
304  va_start(args, msg);
305  char str[1024];
306  vsnprintf(str, 1024, msg, args);
307  va_end(args);
308 
309 #ifdef LIBXML2_6_16
310  // Defined if libxml2 >= 2.6.16
311  int line = xmlSAX2GetLineNumber(state->ctxt);
312 #else
313  int line = getLineNumber(state->ctxt);
314 #endif
315  state->error_msg += "At line: " + long_to_string(line) + ": ";
316  state->error_msg += string(str) + string("\n");
317 }
318 
322 void
323 AISDatabaseParser::aisFatalError(AISParserState *state, const char *msg, ...)
324 {
325  va_list args;
326 
327  state->state = PARSER_ERROR;
328 
329  va_start(args, msg);
330  char str[1024];
331  vsnprintf(str, 1024, msg, args);
332  va_end(args);
333 
334 #ifdef LIBXML2_6_16
335  // Defined if libxml2 >= 2.6.16
336  int line = xmlSAX2GetLineNumber(state->ctxt);
337 #else
338  int line = getLineNumber(state->ctxt);
339 #endif
340  state->error_msg += "At line: " + long_to_string(line) + ": ";
341  state->error_msg += string(str) + string("\n");
342 }
343 
345 
348 static xmlSAXHandler aisSAXParser =
349  {
350  0, // internalSubset
351  0, // isStandalone
352  0, // hasInternalSubset
353  0, // hasExternalSubset
354  0, // resolveEntity
355  (getEntitySAXFunc)AISDatabaseParser::aisGetEntity, // getEntity
356  0, // entityDecl
357  0, // notationDecl
358  0, // attributeDecl
359  0, // elementDecl
360  0, // unparsedEntityDecl
361  0, // setDocumentLocator
362  (startDocumentSAXFunc)AISDatabaseParser::aisStartDocument, // startDocument
363  (endDocumentSAXFunc)AISDatabaseParser::aisEndDocument, // endDocument
364  (startElementSAXFunc)AISDatabaseParser::aisStartElement, // startElement
365  (endElementSAXFunc)AISDatabaseParser::aisEndElement, // endElement
366  0, // reference
367  0, // (charactersSAXFunc)gladeCharacters, characters
368  0, // ignorableWhitespace
369  0, // processingInstruction
370  0, // (commentSAXFunc)gladeComment, comment
371  (warningSAXFunc)AISDatabaseParser::aisWarning, // warning
372  (errorSAXFunc)AISDatabaseParser::aisError, // error
373  (fatalErrorSAXFunc)AISDatabaseParser::aisFatalError, // fatalError
374 #ifdef LIBXML2_5_10
375  0, // getParameterEntity
376  0, // cdataBlock
377  0, // externalSubset
378  0, // initialized
379 #endif
380 #ifdef LIBXML2_6_16
381  0, // _private
382  0, // endElementNs
383  0, // serror
384  0 // startElementNs
385 #endif
386  };
387 
394 void
395 AISDatabaseParser::intern(const string &database, AISResources *ais)
396 {
397  xmlParserCtxtPtr ctxt;
398  AISParserState state;
399 
400  ctxt = xmlCreateFileParserCtxt(database.c_str());
401  if (!ctxt)
402  return;
403 
404  state.ais = ais; // dump values here
405  state.ctxt = ctxt; // need ctxt for error messages
406 
407  ctxt->sax = &aisSAXParser;
408  ctxt->userData = &state;
409  ctxt->validate = true;
410 
411  xmlParseDocument(ctxt);
412 
413  // use getLineNumber and getColumnNumber to make the error messages better.
414  if (!ctxt->wellFormed) {
415  ctxt->sax = NULL;
416  xmlFreeParserCtxt(ctxt);
417  throw AISDatabaseReadFailed(string("\nThe database is not a well formed XML document.\n") + state.error_msg);
418  }
419 
420  if (!ctxt->valid) {
421  ctxt->sax = NULL;
422  xmlFreeParserCtxt(ctxt);
423  throw AISDatabaseReadFailed(string("\nThe database is not a valid document.\n") + state.error_msg);
424  }
425 
426  if (state.state == PARSER_ERROR) {
427  ctxt->sax = NULL;
428  xmlFreeParserCtxt(ctxt);
429  throw AISDatabaseReadFailed(string("\nError parsing AIS resources.\n") + state.error_msg);
430  }
431 
432  ctxt->sax = NULL;
433  xmlFreeParserCtxt(ctxt);
434 }
435 
436 } // namespace libdap
Manage AIS resources.
Definition: AISResources.h:70
STL namespace.
top level DAP object to house generic methods
Definition: AISConnect.cc:30
Associate a rule with an ancillary resource.
Definition: Resource.h:50