libdap++ Updated for version 3.8.2
|
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