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 
00207 void
00208 AISDatabaseParser::aisEndElement(AISParserState *state, const char *)
00209 {
00210     DBG2(cerr << "End element " << name << " (state " << states[state->state]
00211          << ")" << endl);
00212 
00213     switch (state->state) {
00214     case AIS:
00215         state->prev_state = state->state;
00216         state->state = PARSER_FINISH;
00217         break;
00218 
00219     case ENTRY:
00220         state->prev_state = state->state;
00221         state->state = AIS;
00222 
00223         // record 'primary' and 'rv'
00224         if (state->regexp)
00225             state->ais->add_regexp_resource(state->primary, state->rv);
00226         else
00227             state->ais->add_url_resource(state->primary, state->rv);
00228 
00229         // empty rv for the next set of ancillary resources.
00230         state->rv.erase(state->rv.begin(), state->rv.end());
00231         break;
00232 
00233     case PRIMARY:
00234         state->prev_state = state->state;
00235         state->state = ENTRY;
00236         break;
00237 
00238     case ANCILLARY:
00239         state->prev_state = state->state;
00240         state->state = ENTRY;
00241         break;
00242 
00243     case PARSER_UNKNOWN:
00244         // Leave the state and prev_state alone.
00245         state->unknown_depth--;
00246         break;
00247 
00248     case PARSER_ERROR:
00249         break;
00250 
00251     default:
00252         break;
00253     }
00254 }
00255 
00259 xmlEntityPtr
00260 AISDatabaseParser::aisGetEntity(AISParserState *, const xmlChar *name)
00261 {
00262     return xmlGetPredefinedEntity(name);
00263 }
00264 
00269 void
00270 AISDatabaseParser::aisWarning(AISParserState *state, const char *msg, ...)
00271 {
00272     va_list args;
00273 
00274     state->state = PARSER_ERROR;
00275 
00276     va_start(args, msg);
00277     char str[1024];
00278     vsnprintf(str, 1024, msg, args);
00279     va_end(args);
00280 
00281 #ifdef LIBXML2_6_16
00282     // Defined if libxml2 >= 2.6.16
00283     int line = xmlSAX2GetLineNumber(state->ctxt);
00284 #else
00285     int line = getLineNumber(state->ctxt);
00286 #endif
00287     state->error_msg += "At line: " + long_to_string(line) + ": ";
00288     state->error_msg += string(str) + string("\n");
00289 }
00290 
00295 void
00296 AISDatabaseParser::aisError(AISParserState *state, const char *msg, ...)
00297 {
00298     va_list args;
00299 
00300     state->state = PARSER_ERROR;
00301 
00302     va_start(args, msg);
00303     char str[1024];
00304     vsnprintf(str, 1024, msg, args);
00305     va_end(args);
00306 
00307 #ifdef LIBXML2_6_16
00308     // Defined if libxml2 >= 2.6.16
00309     int line = xmlSAX2GetLineNumber(state->ctxt);
00310 #else
00311     int line = getLineNumber(state->ctxt);
00312 #endif
00313     state->error_msg += "At line: " + long_to_string(line) + ": ";
00314     state->error_msg += string(str) + string("\n");
00315 }
00316 
00320 void
00321 AISDatabaseParser::aisFatalError(AISParserState *state, const char *msg, ...)
00322 {
00323     va_list args;
00324 
00325     state->state = PARSER_ERROR;
00326 
00327     va_start(args, msg);
00328     char str[1024];
00329     vsnprintf(str, 1024, msg, args);
00330     va_end(args);
00331 
00332 #ifdef LIBXML2_6_16
00333     // Defined if libxml2 >= 2.6.16
00334     int line = xmlSAX2GetLineNumber(state->ctxt);
00335 #else
00336     int line = getLineNumber(state->ctxt);
00337 #endif
00338     state->error_msg += "At line: " + long_to_string(line) + ": ";
00339     state->error_msg += string(str) + string("\n");
00340 }
00341 
00343 
00346 static xmlSAXHandler aisSAXParser =
00347     {
00348         0, // internalSubset
00349         0, // isStandalone
00350         0, // hasInternalSubset
00351         0, // hasExternalSubset
00352         0, // resolveEntity
00353         (getEntitySAXFunc)AISDatabaseParser::aisGetEntity, // getEntity
00354         0, // entityDecl
00355         0, // notationDecl
00356         0, // attributeDecl
00357         0, // elementDecl
00358         0, // unparsedEntityDecl
00359         0, // setDocumentLocator
00360         (startDocumentSAXFunc)AISDatabaseParser::aisStartDocument, // startDocument
00361         (endDocumentSAXFunc)AISDatabaseParser::aisEndDocument,  // endDocument
00362         (startElementSAXFunc)AISDatabaseParser::aisStartElement,  // startElement
00363         (endElementSAXFunc)AISDatabaseParser::aisEndElement,  // endElement
00364         0, // reference
00365         0, // (charactersSAXFunc)gladeCharacters,  characters
00366         0, // ignorableWhitespace
00367         0, // processingInstruction
00368         0, // (commentSAXFunc)gladeComment,  comment
00369         (warningSAXFunc)AISDatabaseParser::aisWarning, // warning
00370         (errorSAXFunc)AISDatabaseParser::aisError, // error
00371         (fatalErrorSAXFunc)AISDatabaseParser::aisFatalError, // fatalError
00372 #ifdef LIBXML2_5_10
00373         0, // getParameterEntity
00374         0, // cdataBlock
00375         0, // externalSubset
00376         0, // initialized
00377 #endif
00378 #ifdef LIBXML2_6_16
00379         0, // _private
00380         0, // endElementNs
00381         0, // serror
00382         0 // startElementNs
00383 #endif
00384     };
00385 
00392 void
00393 AISDatabaseParser::intern(const string &database, AISResources *ais)
00394 {
00395     xmlParserCtxtPtr ctxt;
00396     AISParserState state;
00397 
00398     ctxt = xmlCreateFileParserCtxt(database.c_str());
00399     if (!ctxt)
00400         return;
00401 
00402     state.ais = ais;  // dump values here
00403     state.ctxt = ctxt;  // need ctxt for error messages
00404 
00405     ctxt->sax = &aisSAXParser;
00406     ctxt->userData = &state;
00407     ctxt->validate = true;
00408 
00409     xmlParseDocument(ctxt);
00410 
00411     // use getLineNumber and getColumnNumber to make the error messages better.
00412     if (!ctxt->wellFormed) {
00413         ctxt->sax = NULL;
00414         xmlFreeParserCtxt(ctxt);
00415         throw AISDatabaseReadFailed(string("\nThe database is not a well formed XML document.\n") + state.error_msg);
00416     }
00417 
00418     if (!ctxt->valid) {
00419         ctxt->sax = NULL;
00420         xmlFreeParserCtxt(ctxt);
00421         throw AISDatabaseReadFailed(string("\nThe database is not a valid document.\n") + state.error_msg);
00422     }
00423 
00424     if (state.state == PARSER_ERROR) {
00425         ctxt->sax = NULL;
00426         xmlFreeParserCtxt(ctxt);
00427         throw AISDatabaseReadFailed(string("\nError parsing AIS resources.\n") + state.error_msg);
00428     }
00429 
00430     ctxt->sax = NULL;
00431     xmlFreeParserCtxt(ctxt);
00432 }
00433 
00434 } // namespace libdap

Generated on Tue Jun 10 18:00:29 2008 for libdap++ by  doxygen 1.5.4