| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * Copyright (c) 1999 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Xerces" and "Apache Software Foundation" must |
| * not be used to endorse or promote products derived from this |
| * software without prior written permission. For written |
| * permission, please contact apache\@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * nor may "Apache" appear in their name, without prior written |
| * permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation, and was |
| * originally based on software copyright (c) 1999, International |
| * Business Machines, Inc., http://www.ibm.com . For more information |
| * on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| */ |
| |
| |
| /** |
| * $Log$ |
| * Revision 1.1 1999/11/09 01:01:12 twl |
| * Initial revision |
| * |
| * Revision 1.5 1999/11/08 20:42:04 rahul |
| * Swat for adding in Product name and CVS comment log variable. |
| * |
| */ |
| |
| |
| // --------------------------------------------------------------------------- |
| // This program is designed to parse an XML file which holds error text |
| // data. It will build a DOM tree from that source file and can output it |
| // a number of different formats. |
| // |
| // In order to drastically simplify the program, it is designed only to run |
| // on platforms/compilers that understand Unicode. It can output the data |
| // in whatever format is required, so it can handle outputting for other |
| // platforms. This also simplifies bootstrapping new releases up on other |
| // platforms. Once the Win32 version is working, it can generate output for |
| // the other platforms so that they can have loadable text from day one. |
| // --------------------------------------------------------------------------- |
| |
| |
| // --------------------------------------------------------------------------- |
| // Includes |
| // --------------------------------------------------------------------------- |
| #include "Xlat.hpp" |
| |
| |
| |
| // --------------------------------------------------------------------------- |
| // Static data |
| // |
| // gRelativeInputPath |
| // This is the path, relative to the given input source root, to the |
| // input file. The given local suffix must also be added to it. |
| // --------------------------------------------------------------------------- |
| static XMLCh* const gRelativeInputPath = L"src/NLS/"; |
| |
| |
| // --------------------------------------------------------------------------- |
| // Global data |
| // --------------------------------------------------------------------------- |
| const XMLCh* typePrefixes[MsgTypes_Count] = |
| { |
| L"W_" |
| , L"E_" |
| , L"V_" |
| }; |
| |
| |
| // --------------------------------------------------------------------------- |
| // Local data |
| // |
| // gLocale |
| // This is the locale suffix, e.g. US_EN, that is used to find the |
| // correct file and can be used on output files as well. Its set via |
| // the /Locale= parameter. |
| // |
| // gOutFormat |
| // This is the output format, which is given on the command line as |
| // /OutFmt= Its mapped to the internal enum which is stored here. |
| // |
| // gOutPath |
| // This is the path to the output path, which is given on the command |
| // line as /OutPath=. Its just the path, not a name, since the output |
| // might consist of multiple output files. They will all be based on |
| // the base part of the input name. |
| // |
| // gSrcRoot |
| // This the path to the root of the build tree. The input files needed |
| // are found in known places relative to it. |
| // --------------------------------------------------------------------------- |
| const XMLCh* gLocale = 0; |
| OutFormats gOutFormat = OutFormat_Unknown; |
| const XMLCh* gOutPath = 0; |
| const XMLCh* gSrcRoot = 0; |
| |
| |
| // --------------------------------------------------------------------------- |
| // Local utility methods |
| // --------------------------------------------------------------------------- |
| |
| // |
| // This method is called to parse the parameters. They must be in this |
| // order and format, for simplicity: |
| // |
| // /SrcRoot=xxx /OutPath=xxx /OutFmt=xxx /Locale=xxx |
| // |
| static bool parseParms(const int argC, XMLCh** argV) |
| { |
| if (argC < 5) |
| return false; |
| |
| unsigned int curParm = 1; |
| |
| if (XMLString::startsWith(argV[curParm], L"/SrcRoot=")) |
| { |
| gSrcRoot = &argV[curParm][9]; |
| } |
| else |
| { |
| wprintf(L"\nExpected /SrcRoot=xxx. Got: %s\n", argV[curParm]); |
| return false; |
| } |
| |
| curParm++; |
| if (XMLString::startsWith(argV[curParm], L"/OutPath=")) |
| { |
| gOutPath = &argV[curParm][9]; |
| } |
| else |
| { |
| wprintf(L"\nExpected /OutPath=xxx. Got: %s\n", argV[curParm]); |
| return false; |
| } |
| |
| |
| curParm++; |
| if (XMLString::startsWith(argV[curParm], L"/OutFmt=")) |
| { |
| const XMLCh* tmpFmt = &argV[curParm][8]; |
| if (!XMLString::compareIString(tmpFmt, L"ResBundle")) |
| gOutFormat = OutFormat_ResBundle; |
| else if (!XMLString::compareIString(tmpFmt, L"Win32RC")) |
| gOutFormat = OutFormat_Win32RC; |
| else if (!XMLString::compareIString(tmpFmt, L"CppSrc")) |
| gOutFormat = OutFormat_CppSrc; |
| else if (!XMLString::compareIString(tmpFmt, L"MsgCat")) |
| gOutFormat = OutFormat_MsgCatalog; |
| else |
| { |
| wprintf(L"\n'%s' is not a legal output format\n", tmpFmt); |
| return false; |
| } |
| } |
| else |
| { |
| wprintf(L"\nExpected /OutFmt=xxx. Got: %s\n", argV[curParm]); |
| return false; |
| } |
| |
| curParm++; |
| if (XMLString::startsWith(argV[curParm], L"/Locale=")) |
| { |
| gLocale = &argV[curParm][8]; |
| } |
| else |
| { |
| wprintf(L"\nExpected /Locale=xxx. Got: %s\n", argV[curParm]); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| |
| static void parseError(const XMLException& toCatch) |
| { |
| wprintf(L"Parse Error:\n ERROR: %s\n\n", toCatch.getMessage()); |
| throw ErrReturn_ParseErr; |
| } |
| |
| |
| static void parseError(const SAXParseException& toCatch) |
| { |
| wprintf(L"Parse Error:\n SAX ERROR: %s\n\n", toCatch.getMessage()); |
| throw ErrReturn_ParseErr; |
| } |
| |
| |
| static void |
| enumMessages( const DOM_Element srcElem |
| , XlatFormatter* const toCall |
| , FILE* const headerFl |
| , const MsgTypes msgType |
| , unsigned int& count) |
| { |
| fwprintf |
| ( |
| headerFl |
| , L" , %s%-30s = %d\n" |
| , typePrefixes[msgType] |
| , L"LowBounds" |
| , count++ |
| ); |
| |
| // |
| // We just run through each of the child elements, each of which is |
| // a Message element. Each one represents a message to output. We keep |
| // a count so that we can output a const value afterwards. |
| // |
| DOM_Node curNode = srcElem.getFirstChild(); |
| while (!curNode.isNull()) |
| { |
| // Skip over text nodes or comment nodes ect... |
| if (curNode.getNodeType() != DOM_Node::ELEMENT_NODE) |
| { |
| curNode = curNode.getNextSibling(); |
| continue; |
| } |
| |
| // Convert it to an element node |
| const DOM_Element& curElem = (const DOM_Element&)curNode; |
| |
| // Ok, this should be a Message node |
| if (!curElem.getTagName().equals(L"Message")) |
| { |
| wprintf(L"Expected a Message node\n\n"); |
| throw ErrReturn_SrcFmtError; |
| } |
| |
| // |
| // Ok, lets pull out the id, text value, and message type. These are |
| // to be passed to the formatter. We have to translate the message |
| // type into one of the offical enum values. |
| // |
| XMLCh* msgText = curElem.getAttribute(L"Text").rawBuffer(); |
| XMLCh* msgId = curElem.getAttribute(L"Id").rawBuffer(); |
| |
| // |
| // Write out an entry to the target header file. These are enums, so |
| // we use the id as the enum name. |
| // |
| fwprintf(headerFl, L" , %-32s = %d\n", msgId, count); |
| |
| // And tell the formatter about this one |
| toCall->nextMessage |
| ( |
| msgText |
| , msgId |
| , count |
| , count |
| ); |
| |
| // Bump the counter, which is also the id assigner |
| count++; |
| |
| // Move to the next child of the source element |
| curNode = curNode.getNextSibling(); |
| } |
| |
| // Write out an upper range bracketing id for this type of error |
| fwprintf |
| ( |
| headerFl |
| , L" , %s%-30s = %d\n" |
| , typePrefixes[msgType] |
| , L"HighBounds" |
| , count++ |
| ); |
| } |
| |
| |
| |
| // --------------------------------------------------------------------------- |
| // Program entry point |
| // --------------------------------------------------------------------------- |
| |
| // |
| // This is the program entry point. It checks the parms, parses the input |
| // file to get a DOM tree, then passes the DOM tree to the appropriate |
| // output method to output the info in a particular format. |
| // |
| extern "C" int wmain(int argC, XMLCh** argV) |
| { |
| try |
| { |
| XMLPlatformUtils::Initialize(); |
| } |
| |
| catch(const XMLException& toCatch) |
| { |
| wprintf(L"Parser init error.\n ERROR: %s\n\n", toCatch.getMessage()); |
| return ErrReturn_ParserInit; |
| } |
| |
| // |
| // Lets check the parameters and save them away in globals for use by |
| // the processing code. |
| // |
| if (!parseParms(argC, argV)) |
| { |
| wprintf(L"Usage:\n NLSXlat /SrcRoot=xx /OutPath=xx /OutFmt=xx /Locale=xx\n\n"); |
| return ErrReturn_BadParameters; |
| } |
| |
| DOM_Document srcDoc; |
| const unsigned int bufSize = 4095; |
| XMLCh tmpFileBuf[bufSize + 1]; |
| try |
| { |
| try |
| { |
| // Build the input file name |
| swprintf |
| ( |
| tmpFileBuf |
| , L"%s%s%s/XMLErrList_%s.Xml" |
| , gSrcRoot |
| , gRelativeInputPath |
| , gLocale |
| , gLocale |
| ); |
| |
| // |
| // Ok, lets invoke the DOM parser on the input file and build |
| // a DOM tree. Turn on validation when we do this. |
| // |
| DOMParser parser; |
| parser.setDoValidation(true); |
| XlatErrHandler errHandler; |
| parser.setErrorHandler(&errHandler); |
| parser.parse(tmpFileBuf); |
| srcDoc = parser.getDocument(); |
| } |
| |
| catch(const XMLException& toCatch) |
| { |
| parseError(toCatch); |
| } |
| |
| // |
| // Use the output format parm to create the correct kind of output |
| // formatter. |
| // |
| XlatFormatter* formatter = 0; |
| switch(gOutFormat) |
| { |
| case OutFormat_CppSrc : |
| formatter = new CppSrcFormatter; |
| break; |
| |
| case OutFormat_Win32RC : |
| formatter = new Win32RCFormatter; |
| break; |
| |
| case OutFormat_MsgCatalog : |
| formatter = new MsgCatFormatter; |
| break; |
| |
| default : |
| wprintf(L"Uknown formatter type enum\n\n"); |
| throw ErrReturn_Internal; |
| } |
| |
| // |
| // Lets handle the root element stuff first. This one holds any over |
| // all information. |
| // |
| DOM_Element rootElem = srcDoc.getDocumentElement(); |
| const XMLCh* localeStr = rootElem.getAttribute(L"Locale").rawBuffer(); |
| |
| // Make sure that the locale matches what we were given |
| if (XMLString::compareString(localeStr, gLocale)) |
| { |
| wprintf(L"The file's locale does not match the target locale\n"); |
| throw ErrReturn_LocaleErr; |
| } |
| |
| // |
| // Get a list of all the MsgDomain children. These each hold one of |
| // the sets of (potentially separately) loadable messages. More |
| // importantly they all have their own error id space. |
| // |
| DOM_NodeList msgSetList = rootElem.getElementsByTagName(L"MsgDomain"); |
| |
| // |
| // Loop through them and look for the domains that we know are |
| // supposed to be there. |
| // |
| const unsigned int count = msgSetList.getLength(); |
| |
| // |
| // Ok, its good enough to get started. So lets call the start output |
| // method on the formatter. |
| // |
| formatter->startOutput(localeStr, gOutPath); |
| |
| // |
| // For each message domain element, we call start and end domain |
| // events bracketed around the loop that sends out each message |
| // in that domain. |
| // |
| // Within each domain, we check for the Warning, Error, and Validity |
| // subelements, and then iterate all the messages in each one. |
| // |
| for (unsigned int index = 0; index < count; index++) |
| { |
| // We know its a DOM Element, so go ahead and cast it |
| DOM_Node curNode = msgSetList.item(index); |
| const DOM_Element& curElem = (const DOM_Element&)curNode; |
| |
| // |
| // Get some of the attribute strings that we need, and transcode |
| // couple that need to be in local format. |
| // |
| const XMLCh* const domainStr = curElem.getAttribute(L"Domain").rawBuffer(); |
| |
| // |
| // Look at the domain and set up our application specific info |
| // that is on a per-domain basis. We need to indicate what the |
| // name of the header is and what the namespace is that they |
| // codes will go into |
| // |
| const XMLCh* headerName = 0; |
| const XMLCh* errNameSpace = 0; |
| if (!XMLString::compareString(domainStr, XMLUni::fgXMLErrDomain)) |
| { |
| headerName = L"XMLErrorCodes.hpp"; |
| errNameSpace = L"XML4CErrs"; |
| } |
| else if (!XMLString::compareString(domainStr, XMLUni::fgValidityDomain)) |
| { |
| headerName = L"XMLValidityCodes.hpp"; |
| errNameSpace = L"XML4CValid"; |
| } |
| else if (!XMLString::compareString(domainStr, XMLUni::fgExceptDomain)) |
| { |
| headerName = L"XMLExceptMsgs.hpp"; |
| errNameSpace = L"XML4CExcepts"; |
| } |
| else |
| { |
| // Not one of ours, so skip it |
| continue; |
| } |
| |
| // |
| // Lets try to create the header file that was indicated for |
| // this domain. |
| // |
| swprintf |
| ( |
| tmpFileBuf |
| , L"%s%s" |
| , gOutPath |
| , headerName |
| ); |
| FILE* outHeader = _wfopen(tmpFileBuf, L"wt+"); |
| if (!outHeader) |
| { |
| wprintf(L"Could not open domain header file: %s\n\n", tmpFileBuf); |
| throw ErrReturn_OutFileOpenFailed; |
| } |
| |
| // |
| // Write out the opening of the class they are nested within, and |
| // the header protection define. |
| // |
| fwprintf(outHeader, L"// This file is generated, don't edit it!!\n\n"); |
| fwprintf(outHeader, L"#if !defined(ERRHEADER_%s)\n", errNameSpace); |
| fwprintf(outHeader, L"#define ERRHEADER_%s\n\n", errNameSpace); |
| |
| // If its not the exception domain, then we need a header included |
| if (XMLString::compareString(domainStr, XMLUni::fgExceptDomain)) |
| fwprintf(outHeader, L"#include <framework/XMLErrorReporter.hpp>\n\n"); |
| |
| fwprintf(outHeader, L"class %s\n{\npublic :\n enum Codes\n {\n", errNameSpace); |
| |
| // Tell the formatter that a new domain is starting |
| formatter->startDomain(domainStr); |
| |
| // |
| // Force out the first message, which is always implicit and is |
| // the 'no error' entry for that domain. |
| // |
| unsigned int count = 0; |
| fwprintf(outHeader, L" %-32s = %d\n", L"NoError", count++); |
| |
| // |
| // Loop through the children of this node, which should take us |
| // through the optional Warning, Error, and Validity subsections. |
| // |
| DOM_Node typeNode = curElem.getFirstChild(); |
| bool typeGotten[3] = { false, false, false }; |
| while (!typeNode.isNull()) |
| { |
| // Skip over text nodes or comment nodes ect... |
| if (typeNode.getNodeType() != DOM_Node::ELEMENT_NODE) |
| { |
| typeNode = typeNode.getNextSibling(); |
| continue; |
| } |
| |
| // Convert it to an element node |
| const DOM_Element& typeElem = (const DOM_Element&)typeNode; |
| |
| // Now get its tag name and convert that to a message type enum |
| DOMString typeName = typeElem.getTagName(); |
| |
| MsgTypes type; |
| if (typeName.equals(L"Warning")) |
| { |
| type = MsgType_Warning; |
| typeGotten[0] = true; |
| } |
| else if (typeName.equals(L"Error")) |
| { |
| if (!XMLString::compareString(domainStr, XMLUni::fgValidityDomain)) |
| { |
| type = MsgType_Validity; |
| typeGotten[2] = true; |
| } |
| else |
| { |
| type = MsgType_Error; |
| typeGotten[1] = true; |
| } |
| } |
| else |
| { |
| wprintf(L"Expected a Warning or Errornode\n\n"); |
| throw ErrReturn_SrcFmtError; |
| } |
| |
| // Call the start message type event |
| formatter->startMsgType(type); |
| |
| // Enumerate the messages under this subsection |
| enumMessages |
| ( |
| typeElem |
| , formatter |
| , outHeader |
| , type |
| , count |
| ); |
| |
| // Call the end message type event |
| formatter->endMsgType(type); |
| |
| // Move to the next child of the source element |
| typeNode = typeNode.getNextSibling(); |
| } |
| |
| // |
| // For any that we did not get, spit out faux boundary |
| // values for it. |
| // |
| for (unsigned int subIndex = 0; subIndex < 3; subIndex++) |
| { |
| if (!typeGotten[subIndex]) |
| { |
| fwprintf |
| ( |
| outHeader |
| , L" , %s%-30s = %d\n" |
| , typePrefixes[subIndex] |
| , L"LowBounds" |
| , count++ |
| ); |
| fwprintf |
| ( |
| outHeader |
| , L" , %s%-30s = %d\n" |
| , typePrefixes[subIndex] |
| , L"HighBounds" |
| , count++ |
| ); |
| } |
| } |
| |
| // Tell the formatter that this domain is ending |
| formatter->endDomain(domainStr, count); |
| |
| // Close out the enum declaration |
| fwprintf(outHeader, L" };\n\n"); |
| |
| // |
| // Generate the code that creates the simple static methods |
| // for testing the error types. We don't do this for the |
| // exceptions header. |
| // |
| if (XMLString::compareString(domainStr, XMLUni::fgExceptDomain)) |
| { |
| fwprintf |
| ( |
| outHeader |
| , L" static bool isFatal(const %s::Codes toCheck)\n" |
| L" {\n" |
| L" return ((toCheck >= E_LowBounds) && (toCheck <= E_HighBounds));\n" |
| L" }\n\n" |
| , errNameSpace |
| ); |
| |
| fwprintf |
| ( |
| outHeader |
| , L" static bool isWarning(const %s::Codes toCheck)\n" |
| L" {\n" |
| L" return ((toCheck >= W_LowBounds) && (toCheck <= W_HighBounds));\n" |
| L" }\n\n" |
| , errNameSpace |
| ); |
| |
| fwprintf |
| ( |
| outHeader |
| , L" static bool isValid(const %s::Codes toCheck)\n" |
| L" {\n" |
| L" return ((toCheck >= V_LowBounds) && (toCheck <= V_HighBounds));\n" |
| L" }\n\n" |
| , errNameSpace |
| ); |
| |
| fwprintf |
| ( |
| outHeader |
| , L" static XMLErrorReporter::ErrTypes errorType(const %s::Codes toCheck)\n" |
| L" {\n" |
| L" if ((toCheck >= W_LowBounds) && (toCheck <= W_HighBounds))\n" |
| L" return XMLErrorReporter::ErrType_Warning;\n" |
| L" else if ((toCheck >= E_LowBounds) && (toCheck <= E_HighBounds))\n" |
| L" return XMLErrorReporter::ErrType_Fatal;\n" |
| L" else if ((toCheck >= V_LowBounds) && (toCheck <= V_HighBounds))\n" |
| L" return XMLErrorReporter::ErrType_Invalid;\n" |
| L" return XMLErrorReporter::ErrTypes_Unknown;\n" |
| L" }\n" |
| , errNameSpace |
| ); |
| } |
| |
| // And close out the class declaration and the header file |
| fwprintf(outHeader, L"};\n#endif\n\n"); |
| fclose(outHeader); |
| } |
| |
| // Ok, we are done so call the end output method |
| formatter->endOutput(); |
| |
| // And clean up the stuff we allocated |
| delete formatter; |
| } |
| |
| catch(const ErrReturns retVal) |
| { |
| return retVal; |
| } |
| |
| // Went ok, so return success |
| return ErrReturn_Success; |
| } |
| |
| |
| |
| // ----------------------------------------------------------------------- |
| // XlatErrHandler: Implementation of the error handler interface |
| // ----------------------------------------------------------------------- |
| void XlatErrHandler::warning(const SAXParseException& toCatch) |
| { |
| parseError(toCatch); |
| } |
| |
| void XlatErrHandler::error(const SAXParseException& toCatch) |
| { |
| parseError(toCatch); |
| } |
| |
| void XlatErrHandler::fatalError(const SAXParseException& toCatch) |
| { |
| parseError(toCatch); |
| } |
| |
| void XlatErrHandler::resetErrors() |
| { |
| } |