AISDatabaseParser.cc

Go to the documentation of this file.
00001 
00002 // -*- mode: c++; c-basic-offset:4 -*-
00003 
00004 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
00005 // Access Protocol.
00006 
00007 // Copyright (c) 2003 OPeNDAP, Inc.
00008 // Author: James Gallagher <jgallagher@opendap.org>
00009 //
00010 // This library is free software; you can redistribute it and/or
00011 // modify it under the terms of the GNU Lesser General Public
00012 // License as published by the Free Software Foundation; either
00013 // version 2.1 of the License, or (at your option) any later version.
00014 //
00015 // This library is distributed in the hope that it will be useful,
00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018 // Lesser General Public License for more details.
00019 //
00020 // You should have received a copy of the GNU Lesser General Public
00021 // License along with this library; if not, write to the Free Software
00022 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00023 //
00024 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
00025 
00026 #include "config.h"
00027 
00028 #include <cstring>
00029 #include <stdarg.h>
00030 
00031 #if 0
00032 // Removed for VC 2008 compatibility. jhrg 4/23/08
00033 #ifdef WIN32
00034 #define vsnprintf _vsnprintf
00035 #endif
00036 #endif
00037 
00038 #include "AISDatabaseParser.h"
00039 #include "util.h"
00040 #include "debug.h"
00041 
00042 using namespace std;
00043 
00044 namespace libdap {
00045 
00046 static const not_used char *states[] =
00047     {
00048         "START",
00049         "FINISH",
00050         "AIS",
00051         "ENTRY",
00052         "PRIMARY",
00053         "ANCILLARY",
00054         "UNKNOWN",
00055         "ERROR"
00056     };
00057 
00064 
00068 void
00069 AISDatabaseParser::aisStartDocument(AISParserState *state)
00070 {
00071     state->state = PARSER_START;
00072     state->unknown_depth = 0;
00073     state->prev_state = PARSER_UNKNOWN;
00074     state->error_msg = "";
00075 
00076     DBG2(cerr << "Parser state: " << states[state->state] << endl);
00077 }
00078 
00081 void
00082 AISDatabaseParser::aisEndDocument(AISParserState *state)
00083 {
00084     DBG2(cerr << "Ending state == " << states[state->state] << endl);
00085 
00086     if (state->unknown_depth != 0) {
00087         AISDatabaseParser::aisFatalError(state, "The document contained unbalanced tags.");
00088 
00089         DBG(cerr << "unknown_depth != 0 (" << state->unknown_depth << ")"
00090             << endl);
00091     }
00092 }
00093 
00102 void
00103 AISDatabaseParser::aisStartElement(AISParserState *state, const char *name,
00104                                    const char **attrs)
00105 {
00106     switch (state->state) {
00107     case PARSER_START:
00108         if (strcmp(name, "ais") != 0) {
00109             DBG(cerr << "Expecting ais.  Got " << name << endl);
00110         }
00111         state->state = AIS;
00112         break;
00113 
00114     case PARSER_FINISH:
00115         break;
00116 
00117     case AIS:
00118         if (strcmp(name, "entry") == 0) {
00119             state->prev_state = state->state;
00120             state->state = ENTRY;
00121         }
00122         else {
00123             state->prev_state = state->state;
00124             state->state = PARSER_UNKNOWN;
00125             state->unknown_depth++;
00126         }
00127         break;
00128 
00129     case ENTRY:
00130         if (strcmp(name, "primary") == 0) {
00131             state->prev_state = state->state;
00132             state->state = PRIMARY;
00133 
00134             if (attrs) {
00135                 if (strcmp(attrs[0], "url") == 0) {
00136                     state->regexp = false;
00137                     state->primary = attrs[1];
00138                 }
00139                 else if (strcmp(attrs[0], "regexp") == 0) {
00140                     state->regexp = true;
00141                     state->primary = attrs[1];
00142                 }
00143             }
00144             else {
00145                 AISDatabaseParser::aisFatalError(state, "Required attribute 'url' or 'regexp' missing from element 'primary'.");
00146                 break;
00147             }
00148         }
00149         else if (strcmp(name, "ancillary") == 0) {
00150             state->prev_state = state->state;
00151             state->state = ANCILLARY;
00152 
00153             string url = ""; // set defaults, MUST have url
00154             string rule = "overwrite";
00155             for (int i = 0; attrs && attrs[i] != 0; i = i + 2) {
00156                 if (strcmp(attrs[i], "url") == 0)
00157                     url = attrs[i+1];
00158                 else if (strcmp(attrs[i], "rule") == 0)
00159                     rule = attrs[i+1];
00160             }
00161 
00162             // If this parser validated the XML, these tests would be
00163             // unnecessary.
00164             if (url == "") {
00165                 AISDatabaseParser::aisFatalError(state, "Required attribute 'url' missing from element 'ancillary'.");
00166                 break;
00167             }
00168 
00169             if (rule != "overwrite" && rule != "replace" && rule != "fallback") {
00170                 string msg = string("Optional attribute 'rule' in element 'ancillary' has a bad value: ") + rule + "\nIt should be one of 'overwrite', 'replace' or 'fallback'.";
00171                 AISDatabaseParser::aisFatalError(state, msg.c_str());
00172                 break;
00173             }
00174 
00175             Resource r(url, rule);
00176             state->rv.push_back(r);
00177         }
00178         else {
00179             state->prev_state = state->state;
00180             state->state = PARSER_UNKNOWN;
00181             state->unknown_depth++;
00182         }
00183         break;
00184 
00185     case PRIMARY:
00186         break;
00187 
00188     case ANCILLARY:
00189         break;
00190 
00191     case PARSER_UNKNOWN:
00192         state->unknown_depth++;
00193         break;
00194 
00195     case PARSER_ERROR:
00196         break;
00197     }
00198 
00199     DBG2(cerr << "Start element " << name << " (state "
00200          << states[state->state] << ")" << endl);
00201 }
00202 
00208 // Although not used in the method itself, name is used in the DBG2
00209 // statement, so we need the parameter name. - pcw 07/08/08
00210 void
00211 AISDatabaseParser::aisEndElement(AISParserState *state, const char *)
00212 {
00213     DBG2(cerr << "End element " << name << " (state " << states[state->state]
00214          << ")" << endl);
00215 
00216     switch (state->state) {
00217     case AIS:
00218         state->prev_state = state->state;
00219         state->state = PARSER_FINISH;
00220         break;
00221 
00222     case ENTRY:
00223         state->prev_state = state->state;
00224         state->state = AIS;
00225 
00226         // record 'primary' and 'rv'
00227         if (state->regexp)
00228             state->ais->add_regexp_resource(state->primary, state->rv);
00229         else
00230             state->ais->add_url_resource(state->primary, state->rv);
00231 
00232         // empty rv for the next set of ancillary resources.
00233         state->rv.erase(state->rv.begin(), state->rv.end());
00234         break;
00235 
00236     case PRIMARY:
00237         state->prev_state = state->state;
00238         state->state = ENTRY;
00239         break;
00240 
00241     case ANCILLARY:
00242         state->prev_state = state->state;
00243         state->state = ENTRY;
00244         break;
00245 
00246     case PARSER_UNKNOWN:
00247         // Leave the state and prev_state alone.
00248         state->unknown_depth--;
00249         break;
00250 
00251     case PARSER_ERROR:
00252         break;
00253 
00254     default:
00255         break;
00256     }
00257 }
00258 
00262 xmlEntityPtr
00263 AISDatabaseParser::aisGetEntity(AISParserState *, const xmlChar *name)
00264 {
00265     return xmlGetPredefinedEntity(name);
00266 }
00267 
00272 void
00273 AISDatabaseParser::aisWarning(AISParserState *state, const char *msg, ...)
00274 {
00275     va_list args;
00276 
00277     state->state = PARSER_ERROR;
00278 
00279     va_start(args, msg);
00280     char str[1024];
00281     vsnprintf(str, 1024, msg, args);
00282     va_end(args);
00283 
00284 #ifdef LIBXML2_6_16
00285     // Defined if libxml2 >= 2.6.16
00286     int line = xmlSAX2GetLineNumber(state->ctxt);
00287 #else
00288     int line = getLineNumber(state->ctxt);
00289 #endif
00290     state->error_msg += "At line: " + long_to_string(line) + ": ";
00291     state->error_msg += string(str) + string("\n");
00292 }
00293 
00298 void
00299 AISDatabaseParser::aisError(AISParserState *state, const char *msg, ...)
00300 {
00301     va_list args;
00302 
00303     state->state = PARSER_ERROR;
00304 
00305     va_start(args, msg);
00306     char str[1024];
00307     vsnprintf(str, 1024, msg, args);
00308     va_end(args);
00309 
00310 #ifdef LIBXML2_6_16
00311     // Defined if libxml2 >= 2.6.16
00312     int line = xmlSAX2GetLineNumber(state->ctxt);
00313 #else
00314     int line = getLineNumber(state->ctxt);
00315 #endif
00316     state->error_msg += "At line: " + long_to_string(line) + ": ";
00317     state->error_msg += string(str) + string("\n");
00318 }
00319 
00323 void
00324 AISDatabaseParser::aisFatalError(AISParserState *state, const char *msg, ...)
00325 {
00326     va_list args;
00327 
00328     state->state = PARSER_ERROR;
00329 
00330     va_start(args, msg);
00331     char str[1024];
00332     vsnprintf(str, 1024, msg, args);
00333     va_end(args);
00334 
00335 #ifdef LIBXML2_6_16
00336     // Defined if libxml2 >= 2.6.16
00337     int line = xmlSAX2GetLineNumber(state->ctxt);
00338 #else
00339     int line = getLineNumber(state->ctxt);
00340 #endif
00341     state->error_msg += "At line: " + long_to_string(line) + ": ";
00342     state->error_msg += string(str) + string("\n");
00343 }
00344 
00346 
00349 static xmlSAXHandler aisSAXParser =
00350     {
00351         0, // internalSubset
00352         0, // isStandalone
00353         0, // hasInternalSubset
00354         0, // hasExternalSubset
00355         0, // resolveEntity
00356         (getEntitySAXFunc)AISDatabaseParser::aisGetEntity, // getEntity
00357         0, // entityDecl
00358         0, // notationDecl
00359         0, // attributeDecl
00360         0, // elementDecl
00361         0, // unparsedEntityDecl
00362         0, // setDocumentLocator
00363         (startDocumentSAXFunc)AISDatabaseParser::aisStartDocument, // startDocument
00364         (endDocumentSAXFunc)AISDatabaseParser::aisEndDocument,  // endDocument
00365         (startElementSAXFunc)AISDatabaseParser::aisStartElement,  // startElement
00366         (endElementSAXFunc)AISDatabaseParser::aisEndElement,  // endElement
00367         0, // reference
00368         0, // (charactersSAXFunc)gladeCharacters,  characters
00369         0, // ignorableWhitespace
00370         0, // processingInstruction
00371         0, // (commentSAXFunc)gladeComment,  comment
00372         (warningSAXFunc)AISDatabaseParser::aisWarning, // warning
00373         (errorSAXFunc)AISDatabaseParser::aisError, // error
00374         (fatalErrorSAXFunc)AISDatabaseParser::aisFatalError, // fatalError
00375 #ifdef LIBXML2_5_10
00376         0, // getParameterEntity
00377         0, // cdataBlock
00378         0, // externalSubset
00379         0, // initialized
00380 #endif
00381 #ifdef LIBXML2_6_16
00382         0, // _private
00383         0, // endElementNs
00384         0, // serror
00385         0 // startElementNs
00386 #endif
00387     };
00388 
00395 void
00396 AISDatabaseParser::intern(const string &database, AISResources *ais)
00397 {
00398     xmlParserCtxtPtr ctxt;
00399     AISParserState state;
00400 
00401     ctxt = xmlCreateFileParserCtxt(database.c_str());
00402     if (!ctxt)
00403         return;
00404 
00405     state.ais = ais;  // dump values here
00406     state.ctxt = ctxt;  // need ctxt for error messages
00407 
00408     ctxt->sax = &aisSAXParser;
00409     ctxt->userData = &state;
00410     ctxt->validate = true;
00411 
00412     xmlParseDocument(ctxt);
00413 
00414     // use getLineNumber and getColumnNumber to make the error messages better.
00415     if (!ctxt->wellFormed) {
00416         ctxt->sax = NULL;
00417         xmlFreeParserCtxt(ctxt);
00418         throw AISDatabaseReadFailed(string("\nThe database is not a well formed XML document.\n") + state.error_msg);
00419     }
00420 
00421     if (!ctxt->valid) {
00422         ctxt->sax = NULL;
00423         xmlFreeParserCtxt(ctxt);
00424         throw AISDatabaseReadFailed(string("\nThe database is not a valid document.\n") + state.error_msg);
00425     }
00426 
00427     if (state.state == PARSER_ERROR) {
00428         ctxt->sax = NULL;
00429         xmlFreeParserCtxt(ctxt);
00430         throw AISDatabaseReadFailed(string("\nError parsing AIS resources.\n") + state.error_msg);
00431     }
00432 
00433     ctxt->sax = NULL;
00434     xmlFreeParserCtxt(ctxt);
00435 }
00436 
00437 } // namespace libdap

Generated on Wed Apr 15 19:20:22 2009 for libdap++ by  doxygen 1.4.7