// balxml_errorinfo.h -*-C++-*- // ---------------------------------------------------------------------------- // NOTICE // // This component is not up to date with current BDE coding standards, and // should not be used as an example for new development. // ---------------------------------------------------------------------------- #ifndef INCLUDED_BALXML_ERRORINFO #define INCLUDED_BALXML_ERRORINFO #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide common error information for XML components. // //@CLASSES: // balxml::ErrorInfo: details of XML parsing errors // //@SEE_ALSO: balxml_reader // //@DESCRIPTION: This component provides an in-core value-semantic class, // 'balxml::ErrorInfo', that contains the following diagnostic information: //.. // - severity (WARNING, ERROR, FATAL ERROR) // - line number // - column number // - document source (name of document containing the error) // - error message (error description) //.. // Parsing components in the 'baexml' package make use of 'balxml::ErrorInfo' // as a standard way to report errors and warnings back to the caller. The // information contained within a 'balxml::ErrorInfo' object is sufficient to // report a single diagnostic in an input document. // ///Usage ///----- // In this example, we create a parser for a simple file of percentages. The // file is formatted as a sequence of lines, with each line containing a // decimal number in the range "0" to "100", inclusive. Leading whitespace and // blank lines are ignored. When an error occurs during parsing, the error // data is stored in a 'balxml::ErrorInfo' object. Our parser's interface is // as follows: //.. // #include <balxml_errorinfo.h> // #include <sstream> // #include <cstdlib> // #include <cstring> // #include <climits> // // class PercentParser { // // Parse a document stream consisting of a sequence of integral // // percentages (0 to 100) in decimal text format, one per line. // // bsl::istream *d_input; // Input document stream // bsl::string d_docName; // Document name // int d_line; // Current line number // // public: // PercentParser(bsl::istream *input, // const bsl::string& docName = "INPUT"); // // Construct a parser to parse the data in the specified 'input' // // stream having the (optional) specified 'docName'. A valid // // 'input' stream contains a sequence of integers in the range 0 // // to 100, one per line, in decimal text format. Each line may // // contain leading but not trailing tabs and spaces. Characters // // after the 20th character on each line are ignored and will // // generate a warning. // // int parseNext(balxml::ErrorInfo *errorInfo); // // Read and parse the next percentage in the input stream and // // return the percentage or -1 on eof or -2 on error. Set the // // value of the specified 'errorInfo' structure on error or // // warning and leave it unchanged otherwise. Do nothing and // // return -2 if 'errorInfo->severity()' >= 'BAEXML_ERROR'. // }; //.. // The constructor is straight-forward: //.. // PercentParser::PercentParser(bsl::istream *input, // const bsl::string& docName) // : d_input(input), d_docName(docName), d_line(0) // { // } //.. // The 'parseNext' function begins by testing if a previous error occurred. By // testing this condition, we can call 'parseNext' several times, knowing that // the first error will stop the parse operation. //.. // int PercentParser::parseNext(balxml::ErrorInfo *errorInfo) // { // static const int MAX_LINE = 20; // // if (errorInfo->isAnyError()) { // // Don't advance if errorInfo shows a previous error. // return -2; // } //.. // The parser skips leading whitespace and lines containing only whitespace. // It loops until a non-empty line is found: //.. // char buffer[MAX_LINE + 1]; // buffer[0] = '\0'; // // // Skip empty lines empty line // int len = 0; // int startColumn = 0; // while (startColumn == len) { // ++d_line; // d_input->getline(buffer, MAX_LINE + 1, '\n'); // len = bsl::strlen(buffer); //.. // The input stream reports that the input line is longer than 'MAX_LINE' by // setting the fail() condition. In this case, we set the error object to a // warning state, indicating the line and column where the problem occurred. // Then we clear the stream condition and discard the rest of the line. //.. // if (MAX_LINE == len && d_input->fail()) { // // 20 characters read without encountering newline. // // Warn about long line and discard rest of line. // errorInfo->setError(balxml::ErrorInfo::BAEXML_WARNING, // d_line, len, d_docName, // "Text after 20th column was discarded"); // d_input->clear(); // d_input->ignore(INT_MAX, '\n'); // } //.. // If we detect an EOF condition, we just return -1. Otherwise, we skip the // leading whitespace and go on. //.. // else if (0 == len && d_input->eof()) { // // Encountered eof before any other characters. // return -1; // } // // // Skip leading whitespace // startColumn = bsl::strspn(buffer, " \t"); // } // //.. // Now we perform two more error checks: one or superfluous characters after // the integer, the other for an out-of-range integer. If the 'errorInfo' // object is already in warning state, either of these errors will overwrite // the existing warning with the new error condition. //.. // char *endp = 0; // long result = bsl::strtol(buffer + startColumn, &endp, 10); // int endColumn = endp - buffer; // if (endColumn < len) { // // Conversion did not consume rest of buffer. // errorInfo->setError(balxml::ErrorInfo::BAEXML_ERROR, // d_line, endColumn + 1, d_docName, // "Bad input character"); // return -2; // } else if (result < 0 || 100 < result) { // // Range error. // errorInfo->setError(balxml::ErrorInfo::BAEXML_ERROR, // d_line, startColumn + 1, d_docName, // "Value is not between 0 and 100"); // return -2; // } //.. // If there were no errors, return the result. Note that the 'errorInfo' // object may contain a warning, but warnings typically do not cause a change // in the error value. //.. // return result; // } //.. // The main program uses the 'PercentParser' class to parse a list of values // and compute the average. Typically, the data would be stored in a file, // but we'll use a literal string for demonstration purposes: //.. // int main() // { // static const char INPUTS[] = // " 20\n" // OK // " 30\n" // Warning ('0' truncated) // " \n" // Skipped: empty line // "99x\n" // Error: bad character // " 101\n" // Error: out of range // " 1010\n"; // Out-of-range overrides warning //.. // We convert the string into a stream and initialize the parser. We name our // input stream "Inputs" for the purpose of error handling. We also // initialize our working variables: //.. // bsl::istringstream inputstream(INPUTS); // PercentParser parser(&inputstream, "Inputs"); // int result; // int sum = 0; // int numValues = 0; //.. // Any error in parsing will be stored in the 'errorInfo' object. When first // constructed, it has a severity of 'BAEXML_NO_ERROR'. //.. // balxml::ErrorInfo errorInfo; // assert(errorInfo.isNoError()); //.. // Normally, parsing would proceed in a loop. However, to illustrate the // different error-handling situations, we have unrolled the loop below. // // The first parse succeeds, and no error is reported: //.. // result = parser.parseNext(&errorInfo); // assert(20 == result); // assert(errorInfo.isNoError()); // sum += result; // ++numValues; //.. // The next parse also succeeds but, because the input line was very long, a // warning was generated: //.. // result = parser.parseNext(&errorInfo); // assert(3 == result); // Truncated at 20th column // assert(errorInfo.isWarning()); // assert(2 == errorInfo.lineNumber()); // assert(20 == errorInfo.columnNumber()); // assert("Text after 20th column was discarded" == errorInfo.message()); // sum += result; // ++numValues; //.. // After resetting the 'errorInfo' object, the we call 'nextParse' again. This // time it fails with an error. The line, column, and source of the error are // reported in the object. //.. // errorInfo.reset(); // result = parser.parseNext(&errorInfo); // assert(-2 == result); // assert("Inputs" == errorInfo.source()); // assert(errorInfo.isError()); // assert(4 == errorInfo.lineNumber()); // assert(3 == errorInfo.columnNumber()); // assert("Bad input character" == errorInfo.message()); //.. // If the 'errorInfo' object is not reset, calling 'parseNext' becomes a // no-op: //.. // result = parser.parseNext(&errorInfo); // assert(-2 == result); // assert(errorInfo.isError()); // assert(4 == errorInfo.lineNumber()); // assert(3 == errorInfo.columnNumber()); // assert("Bad input character" == errorInfo.message()); //.. // After calling 'reset', the next call to 'parseNext' produces a different // error message: //.. // errorInfo.reset(); // result = parser.parseNext(&errorInfo); // assert(-2 == result); // assert(errorInfo.isError()); // assert(5 == errorInfo.lineNumber()); // assert(6 == errorInfo.columnNumber()); // assert("Value is not between 0 and 100" == errorInfo.message()); //.. // The last line of the file contains two problems: a long line, which would // produce a warning, and a range error, which would produce an error. The // warning message is overwritten by the error message because the error has a // higher severity. Therefore, on return from 'parseNext', only the error // message is stored in 'errorInfo' and the warning is lost: //.. // errorInfo.reset(); // result = parser.parseNext(&errorInfo); // assert(-2 == result); // assert(errorInfo.isError()); // assert(6 == errorInfo.lineNumber()); // assert(18 == errorInfo.columnNumber()); // assert("Value is not between 0 and 100" == errorInfo.message()); //.. // Writing the 'errorInfo' object to a log or file will produce a readable // error message: //.. // bsl::cerr << errorInfo << bsl::endl; //.. // The resulting message to standard error looks as follows: //.. // Inputs:6.18: Error: Value is not between 0 and 100 //.. // Finally, we reach the end of the input stream and can compute our average. //.. // errorInfo.reset(); // result = parser.parseNext(&errorInfo); // assert(-1 == result); // assert(errorInfo.isNoError()); // // int average = sum / numValues; // assert(11 == average); // (20 + 3) / 2 // // return 0; // } //.. #include <balscm_version.h> #include <bslma_allocator.h> #include <bslma_usesbslmaallocator.h> #include <bslmf_nestedtraitdeclaration.h> #include <bsl_ostream.h> #include <bsl_string.h> namespace BloombergLP { namespace balxml { // =============== // class ErrorInfo // =============== class ErrorInfo { // This class provides detailed information for errors encountered during // XML parsing. Such information is common for most parsers and contains // the following data: line number, column number, severity code, // identification of source document and the parser error message. public: // PUBLIC TYPES enum Severity { // Error severity level. Each severity level is considered more severe // than the one before. Client software will typically continue // processing on 'BAEXML_WARNING' and will stop processing on // 'BAEXML_ERROR' or 'BAEXML_FATAL_ERROR'. The distinction between the // latter two severities is somewhat arbitrary. A component that sets // the severity of a 'ErrorInfo' object can use 'BAEXML_FATAL_ERROR' to // discard a less-severe error with 'BAEXML_ERROR'. As a general // guideline, 'BAEXML_ERROR' means that processing could continue, // albeit with compromised results and 'BAEXML_FATAL_ERROR' means that // processing could not continue. For example, a constraint error // would typically have 'BAEXML_ERROR' whereas a parsing (syntax) error // would have 'BAEXML_FATAL_ERROR'. e_NO_ERROR, e_WARNING, e_ERROR, e_FATAL_ERROR #ifndef BDE_OMIT_INTERNAL_DEPRECATED , BAEXML_NO_ERROR = e_NO_ERROR , BAEXML_WARNING = e_WARNING , BAEXML_ERROR = e_ERROR , BAEXML_FATAL_ERROR = e_FATAL_ERROR #endif // BDE_OMIT_INTERNAL_DEPRECATED }; private: // PRIVATE DATA MEMBERS Severity d_severity; int d_lineNumber; int d_columnNumber; bsl::string d_source; bsl::string d_message; public: // TRAITS BSLMF_NESTED_TRAIT_DECLARATION(ErrorInfo, bslma::UsesBslmaAllocator); // CREATORS ErrorInfo(bslma::Allocator *basicAllocator = 0); // Construct an error info object using the (optionally) specified // 'basicAllocator', or the default allocator of none is specified. // After construction, 'severity()' will return 'BAEXML_NO_ERROR', // 'lineNumber()' and 'columnNumber()' will each return 0, and // 'source()' and 'message()' will each return an empty string. ErrorInfo(const ErrorInfo& other, bslma::Allocator *basicAllocator = 0); // Construct a copy of the specified 'other' object using the // (optionally) specified 'basicAllocator', or the default allocator // of none is specified. ~ErrorInfo(); // Destroy this object. // MANIPULATORS ErrorInfo& operator=(const ErrorInfo& rhs); // Copy the value of the specified 'rhs' object into this object and // return a modifiable reference to this object. void setError(Severity severity, int lineNumber, int columnNumber, const bsl::string_view& source, const bsl::string_view& errorMsg); // If the specified 'severity' is greater than the current value of // 'this->severity()', then set this object's severity to 'severity', // line number to the specified 'lineNumber', column number to the // specified 'columnNumber', source name to the specified 'source', and // error message to the specified 'errorMsg', otherwise do nothing. void setError(const ErrorInfo& other); // If the severity of the specified 'other' object is greater than the // current value of 'this->severity()', then assign this object the // value of 'other', otherwise do nothing. void reset(); // Reset this object to initial state, as if it were default // constructed. // ACCESSORS bool isNoError() const; // Return true if the 'severity() == 'BAEXML_NO_ERROR' and false // otherwise. bool isWarning() const; // Return true if the 'severity() == 'BAEXML_WARNING' and false // otherwise. bool isError() const; // Return true if the 'severity() == 'BAEXML_ERROR' and false // otherwise. bool isFatalError() const; // Return true if the 'severity() == 'BAEXML_FATAL_ERROR' and false // otherwise. bool isAnyError() const; // Return true if the 'severity() >= 'BAEXML_ERROR' (i.e., // 'BAEXML_ERROR' or 'BAEXML_FATAL_ERROR') and false otherwise. Severity severity() const; // Return the severity level. int lineNumber() const; // Return the line number of the warning or error. By convention, the // first line is numbered 1. The constructors and 'reset' functions // set the line number to 0, since there is no error line to report. int columnNumber() const; // Return the column number. By convention, the first column is // numbered 1. The constructors and 'reset' functions set the column // number to 0, since there is no error column to report. const bsl::string& source() const; // Return the string that identifies the document being parsed. const bsl::string& message() const; // Return the string describing the error or warning. }; // FREE OPERATORS bool operator==(const ErrorInfo& lhs, const ErrorInfo& rhs); // Return true if the specified 'lhs' object has the same value as the // specified 'rhs' object. The two objects have the same value if // 'severity()', 'lineNumber()', 'columnNumber()', 'source()', and // 'message()' return equal values for both. inline bool operator!=(const ErrorInfo& lhs, const ErrorInfo& rhs); // Return true if the specified 'lhs' object does not have the same value // as the specified 'rhs' object. The two objects have the same value if // 'severity()', 'lineNumber()', 'columnNumber()', 'source()', and // 'message()' return equal values for both. bsl::ostream& operator<<(bsl::ostream& stream, const ErrorInfo& errInfo); // Print the specified 'errInfo' object to the specified 'stream' in // human-readable form and return a modifiable reference to 'stream'. The // output is one-line without a terminating newline. // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // --------------- // class ErrorInfo // --------------- // MANIPULATORS inline void ErrorInfo::setError(const ErrorInfo& other) { if (other.d_severity > d_severity) { *this = other; } } // ACCESSORS inline ErrorInfo::Severity ErrorInfo::severity() const { return d_severity; } inline bool ErrorInfo::isNoError() const { return d_severity == e_NO_ERROR; } inline bool ErrorInfo::isWarning() const { return d_severity == e_WARNING; } inline bool ErrorInfo::isError() const { return d_severity == e_ERROR; } inline bool ErrorInfo::isFatalError() const { return d_severity == e_FATAL_ERROR; } inline bool ErrorInfo::isAnyError() const { return d_severity >= e_ERROR; } inline int ErrorInfo::lineNumber() const { return d_lineNumber; } inline int ErrorInfo::columnNumber() const { return d_columnNumber; } inline const bsl::string & ErrorInfo::source() const { return d_source; } inline const bsl::string & ErrorInfo::message() const { return d_message; } } // close package namespace // FREE OPERATORS inline bool balxml::operator!=(const ErrorInfo& lhs, const ErrorInfo& rhs) { return ! (lhs == rhs); } } // close enterprise namespace #endif // INCLUDED_BALXML_ERRORINFO // ---------------------------------------------------------------------------- // Copyright 2015 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------