ResourceBundle.cpp

Go to the documentation of this file.
00001 /*
00002 // $Id: //open/dev/fennel/common/ResourceBundle.cpp#14 $
00003 // Fennel is a library of data storage and processing components.
00004 // Copyright (C) 2005-2009 The Eigenbase Project
00005 // Copyright (C) 2004-2009 SQLstream, Inc.
00006 // Copyright (C) 2005-2009 LucidEra, Inc.
00007 //
00008 // This program is free software; you can redistribute it and/or modify it
00009 // under the terms of the GNU General Public License as published by the Free
00010 // Software Foundation; either version 2 of the License, or (at your option)
00011 // any later version approved by The Eigenbase Project.
00012 //
00013 // This program is distributed in the hope that it will be useful,
00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 // GNU General Public License for more details.
00017 //
00018 // You should have received a copy of the GNU General Public License
00019 // along with this program; if not, write to the Free Software
00020 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021 */
00022 
00023 #include "fennel/common/CommonPreamble.h"
00024 #include "fennel/common/ResourceBundle.h"
00025 
00026 #include <algorithm>
00027 #include <cstdlib>
00028 #include <fstream>
00029 #include <map>
00030 #include <set>
00031 #include <string>
00032 #include <sstream>
00033 
00034 #include <boost/lexical_cast.hpp>
00035 
00036 FENNEL_BEGIN_CPPFILE("$Id: //open/dev/fennel/common/ResourceBundle.cpp#14 $");
00037 
00038 using namespace std;
00039 using namespace boost;
00040 
00041 static string globalResourceLocation("");
00042 
00043 RecursiveMutex ResourceBundle::mutex;
00044 
00045 RecursiveMutex &ResourceBundle::getMutex()
00046 {
00047     return mutex;
00048 }
00049 
00050 void ResourceBundle::setGlobalResourceFileLocation(const string &location)
00051 {
00052     globalResourceLocation = location;
00053 }
00054 
00055 ResourceBundle::ResourceBundle(
00056     const string &baseName,
00057     const Locale &locale,
00058     const string &location)
00059     : _baseName(baseName),
00060       _locale(locale),
00061       _location(location),
00062       _parent(NULL)
00063 {
00064     loadMessages();
00065 }
00066 
00067 ResourceBundle::~ResourceBundle()
00068 {
00069 }
00070 
00071 void ResourceBundle::setParent(ResourceBundle *bundle)
00072 {
00073     _parent = bundle;
00074 }
00075 
00076 const set<string> ResourceBundle::getKeys() const
00077 {
00078     set<string> keys;
00079 
00080     map<string, string>::const_iterator iter = _messages.begin(),
00081         end = _messages.end();
00082 
00083     while (iter != end) {
00084         keys.insert((*iter).first);
00085         iter++;
00086     }
00087 
00088     if (_parent) {
00089         set<string> parentKeys = _parent->getKeys();
00090 
00091         keys.insert(parentKeys.begin(), parentKeys.end());
00092     }
00093 
00094     return keys;
00095 }
00096 
00097 static string MISSING_KEY("[[unknown key]]");
00098 
00099 const string &ResourceBundle::getMessage(const string &key) const
00100 {
00101     map<string, string>::const_iterator iter;
00102     iter = _messages.find(key);
00103     if (iter == _messages.end()) {
00104         if (_parent) {
00105             return _parent->getMessage(key);
00106         }
00107 
00108         return MISSING_KEY;
00109     }
00110 
00111     return (*iter).second;
00112 }
00113 
00114 
00115 bool ResourceBundle::hasMessage(const string &key) const
00116 {
00117     return
00118         _messages.find(key) != _messages.end()
00119         || (_parent && _parent->hasMessage(key));
00120 }
00121 
00122 const Locale &ResourceBundle::getLocale() const
00123 {
00124     return _locale;
00125 }
00126 
00127 
00128 const string &ResourceBundle::getBaseName() const
00129 {
00130     return _baseName;
00131 }
00132 
00133 
00134 static const char APOS = '\'';
00135 static const char LEFT_BRACE = '{';
00136 static const char RIGHT_BRACE = '}';
00137 static const char COMMA = ',';
00138 
00139 static string convertPropertyToBoost(string &message)
00140 {
00141     stringstream ss;
00142 
00143     bool quoted = false;
00144     char ch;
00145     char nextCh;
00146 
00147     for (int i = 0, n = message.length(); i < n; i++) {
00148         ch = message[i];
00149         nextCh = (i + 1 < n) ? message[i + 1] : 0;
00150 
00151         if (ch == APOS) {
00152             if (nextCh != APOS) {
00153                 // Bare apostrophes signal start/end of QuotedString in the BNF
00154                 quoted = !quoted;
00155                 continue;
00156             }
00157 
00158             // Quoted or not, the next character is an apostrophe, so we
00159             // output an apostrophe.
00160             ss << APOS;
00161             i++; // skip nextCh
00162             continue;
00163         }
00164 
00165         if (quoted || ch != LEFT_BRACE) {
00166             ss << ch;
00167             continue;
00168         }
00169 
00170         // Handle an argument
00171         string::size_type commaPos = message.find(COMMA, i);
00172         string::size_type bracketPos = message.find(RIGHT_BRACE, i);
00173         if (bracketPos == string::npos) {
00174             // Bad format -- give up
00175             return message;
00176         }
00177 
00178         int argEndIndex = (commaPos == string::npos
00179                            ? bracketPos
00180                            : min(commaPos, bracketPos));
00181 
00182         int argIndex = boost::lexical_cast<int>(
00183             message.substr(i + 1, argEndIndex - (i + 1)));
00184 
00185         // Boost args are 1-based
00186         ss << '%' << (argIndex + 1) << '%';
00187 
00188         // Find the end of the argument tag
00189         bool quotedPattern = false;
00190         int bracketDepth = 0;
00191         i = argEndIndex;
00192 
00193         bool done = false;
00194         while (!done && i < n) {
00195             ch = message[i];
00196 
00197             switch (ch) {
00198             default:
00199                 i++;
00200                 break;
00201 
00202             case APOS:
00203                 quotedPattern = !quotedPattern;
00204                 i++;
00205                 break;
00206 
00207             case LEFT_BRACE:
00208                 if (!quotedPattern) {
00209                     bracketDepth++;
00210                 }
00211                 i++;
00212                 break;
00213 
00214             case RIGHT_BRACE:
00215                 if (!quotedPattern) {
00216                     if (bracketDepth > 0) {
00217                         bracketDepth--;
00218                     } else {
00219                         // end of pattern!
00220                         done = true;
00221                         break;
00222                     }
00223                 }
00224                 i++;
00225                 break;
00226             }
00227         }
00228 
00229         if (i == n) {
00230             // couldn't find end of pattern -- give up
00231             return message;
00232         }
00233     }
00234 
00235     return ss.str();
00236 }
00237 
00238 // NOTE jvs 18-Feb-2004:  The conversion from Java resource format
00239 // to Boost format could be done just once as part of the build, instead
00240 // of each time on startup.  However, keeping everything in Java format
00241 // simplifies the packaging/translation/distribution process.  And the
00242 // performance hit should be minuscule unless the number of
00243 // messages is huge.
00244 
00245 void ResourceBundle::loadMessages()
00246 {
00247     fstream in;
00248 
00249     // e.g. GeneratedResourceBundle_en_US.resources
00250     string fileName;
00251 
00252     if (_locale == Locale("")) {
00253         fileName.assign(_baseName
00254                         + ".properties");
00255     } else {
00256         fileName.assign(_baseName
00257                         + "_"
00258                         + _locale.getDisplayName()
00259                         + ".properties");
00260     }
00261 
00262     // look in _location first
00263     bool tryGlobalLocation = true;
00264     if (!_location.empty()) {
00265         string path = _location + "/" + fileName;
00266         in.open(path.c_str(), ios::in);
00267         if (in.good()) {
00268             tryGlobalLocation = false;
00269         }
00270     }
00271 
00272     // failing that, try the gobal location, if any
00273     if (tryGlobalLocation) {
00274         bool tryEnvVar = true;
00275 
00276         // TODO jvs 18-Feb-2004: once Fennel starts using Boost's
00277         // platform-independent filesystem library, use it here too.
00278         if (!globalResourceLocation.empty()) {
00279             string path = globalResourceLocation + "/" + fileName;
00280             in.open(path.c_str(), ios::in);
00281             if (in.good()) {
00282                 tryEnvVar = false;
00283             }
00284         }
00285 
00286         if (tryEnvVar) {
00287             const char *fennelHome = getenv("FENNEL_HOME");
00288             if (fennelHome == NULL) {
00289                 return; // give up
00290             }
00291 
00292             string path = string(fennelHome) + "/common/" + fileName;
00293             in.open(path.c_str(), ios::in);
00294             if (!in.good()) {
00295                 return; // give up
00296             }
00297         }
00298     }
00299 
00300     string line, key, message;
00301     while (in.good()) {
00302         getline(in, line);
00303 
00304         if (line.length() == 0 || line[0] == '#') {
00305             // ignore blank lines and comments
00306             continue;
00307         }
00308 
00309         string::size_type pos = line.find('=');
00310         if (pos == string::npos) {
00311             // bad message format?
00312             continue;
00313         }
00314 
00315         key = line.substr(0, pos);
00316         message = line.substr(pos + 1);
00317 
00318         _messages[key] = convertPropertyToBoost(message);
00319     }
00320 
00321     in.close();
00322 }
00323 
00324 FENNEL_END_CPPFILE("$Id: //open/dev/fennel/common/ResourceBundle.cpp#14 $");
00325 
00326 // End ResourceBundle.cpp

Generated on Mon Jun 22 04:00:18 2009 for Fennel by  doxygen 1.5.1