util/Property.cpp

Go to the documentation of this file.
00001 
00002 #ifndef _UTIL_PROPERTY_C
00003 #define _UTIL_PROPERTY_C
00004 
00005 #include "Property.h"
00006 #include <cstdlib> //<stdlib.h>
00007 #include <fstream>
00008 #include <iostream>
00009 #include <util/lexical_cast.h>
00010 #include <util/PropertyDef.h>
00011 #include <util/Constants.h>
00012 #include <util/StringTrim.h>
00013 
00014 using namespace std;
00015 using namespace org::xmlBlaster::util;
00016 
00017 #if defined(WINCE) /* No getenv() for Windows CE. TODO: Use some registry workaround as others do */
00018 char *getenv(const char *key) {
00019       return 0;
00020 }
00021 #endif
00022 
00023 #define  MAX_NEST 50
00024 
00025 Property::Property(int args, const char * const argv[]) : properties_() {
00026    initializeDefaultProperties();
00027    if (args && argv) {
00028       loadCommandLineProps(args, argv, std::string("-"), false); // xmlBlaster-style properties
00029    }
00030    //loadPropertyFile();
00031 }
00032 
00033 Property::Property(MapType propMap) : properties_(propMap)
00034 {
00035    initializeDefaultProperties();
00036         replaceVariables(true);
00037 }
00038 
00039 void Property::initializeDefaultProperties()
00040 {
00041    // Add some predefined variables to be useful in xmlBlaster.properties as ${user.home} etc.
00042    bool useEnv = true;
00043    bool overwrite = false;
00044    
00045 #if defined(_WINDOWS)
00046    // Windows: _WINDOWS
00047    // HOMEDRIVE=C:
00048    // HOMEPATH=\Documents and Settings\Marcel
00049    char *driveP = getenv("HOMEDRIVE");
00050    string drive = (driveP != 0) ? string(driveP) : string("");
00051    char *pathP = getenv("HOMEPATH");
00052    string path = (pathP != 0) ? string(pathP) : string("");
00053    setProperty("user.home", drive + path);
00054 #else
00055    if (!propertyExists("user.home", false)) {
00056       string value = getProperty("HOME", useEnv);
00057       if (value != "") {
00058          setProperty("user.home", value, true); // UNIX
00059       }
00060    }
00061 #endif
00062    //std::cout << "user.home=" << getProperty("user.home", "??") << std::endl;
00063 
00064    if (!propertyExists("user.name", false)) {
00065       string value = getProperty("USER", useEnv);
00066       if (value != "") {
00067          setProperty("user.name", value, true); // UNIX
00068       }
00069       else {
00070          // Windows: _WINDOWS
00071          // USERNAME=joe
00072          char *puser = getenv("USERNAME");
00073          if (puser) {
00074             string tmp = puser;
00075             setProperty("user.name", tmp, true);
00076          }
00077          else {
00078             // HOMEPATH=\Documents and Settings\Marcel
00079             char *pathP = getenv("HOMEPATH");
00080             string path = (pathP != 0) ? string(pathP) : string("");
00081             std::string::size_type pos = path.rfind(FILE_SEP);
00082             if (pos != string::npos && pos < path.size()-1) {
00083                setProperty("user.name", path.substr(pos+1));
00084             }
00085          }
00086       }
00087    }
00088    
00089    if (!propertyExists("java.io.tmpdir", false)) {
00090       string value = getProperty("TMP", useEnv);
00091       if (value != "") {
00092          setProperty("java.io.tmpdir", value, true);
00093       }
00094    }
00095 
00096    // XMLBLASTER_HOME
00097 
00098    setProperty("file.separator", FILE_SEP, overwrite);  // '/' on UNIX
00099    setProperty("path.separator", PATH_SEP, overwrite);  // ':' on UNIX
00100 
00101    // _WINDOWS:
00102    // COMPUTERNAME=myserver
00103    // LOGONSERVER=\\myserver
00104    // NUMBER_OF_PROCESSORS=1
00105    // OS=Windows_NT
00106    // PROCESSOR_ARCHITECTURE=x86
00107    // PROCESSOR_IDENTIFIER=x86 Family 15 Model 2 Stepping 4, GenuineIntel
00108    // PROCESSOR_LEVEL=15
00109    // PROCESSOR_REVISION=0204
00110    // USERDOMAIN=myserver
00111    // USERNAME=joe
00112    // USERPROFILE=C:\Documents and Settings\marcel
00113    // VC7=1
00114    // HOMEDRIVE=C:
00115    // HOMEPATH=\Documents and Settings\marcel
00116    // TMP=C:\DOCUME~1\marcel\LOCALS~1\Temp
00117 
00118    // os.name = Linux
00119    // os.name = "Windows XP"     os.version=5.1 (_WINDOWS)
00120    // line.separator = CRLF ...
00121    // java.io.tmpdir = /tmp ...   C:\DOCUME~1\marcel\LOCALS~1\Temp\ == C:\Documents and Settings\marcel\Local Settings\Temp  (_WINDOWS)
00122    // user.dir = /home/xmlblast/test -> getcwd()
00123    // sun.cpu.endian = little
00124 }
00125 
00129 int 
00130 Property::loadCommandLineProps(int args,
00131                                const char * const argv[],
00132                                const string &prefix, 
00133                                bool javaStyle) 
00134 {
00135 
00136    int    count = 1, ret=0, nmax = args;
00137    string value;
00138    //if (!javaStyle) nmax--; // they come in separated pairs
00139    while (count < nmax) {
00140       string name = argv[count];
00141       if (name.find(prefix) == 0) { // it is a property
00142          name = name.substr(prefix.length()); // remove separator e.g. "-trace" -> "trace"
00143          if (!javaStyle) { // Corba style (or other non-java styles)
00144             //if (name == "h" || name == "help" || name == "?" ) {
00145             value = (count < nmax-1) ? argv[count+1] : "";
00146             if (value == ""/* || value.find(prefix) == 0*/) { // A property without a value -> we set it to true, for example --help
00147                if (setProperty_(name, lexical_cast<std::string>(true))) ret++;
00148             }
00149             else {
00150                count++;
00151                //std::cout << "readPropertyCommandLine: " << name << "=" << value << std::endl;
00152                if (setProperty_(name, value)) ret++;
00153             }
00154          }
00155          else { // java style: prop1=val1
00156             pair<const string, string> propPair(getPair(name));
00157             if (setProperty_(propPair.first, propPair.second)) ret++;
00158          }
00159       }
00160       count++;
00161    }
00162 //   if (count > 0)
00163 //      std::cout << "Successfully read " << (count-1)/2 << " command line arguments" << std::endl;
00164    if (ret > 0) {
00165       replaceVariables(true);
00166    }
00167    return ret;
00168 }
00169 
00170 
00171 /*
00172  * xmlBlaster.properties is searched in this sequence:
00173  * <ul>
00174  *    <li>the command line parameter '-propertyFile', e.g. "-propertyFile /tmp/xy.properties"</li>
00175  *    <li>the environment variable 'propertyFile', e.g. "propertyFile=/tmp/xy.properties"</li>
00176  *    <li>the local directory: ./xmlBlaster.properties</li>
00177  *    <li>in your home directory, HOME/xmlBlaster.properties</li>
00178  *    <li>in the $XMLBLASTER_HOME directory</li>
00179  * </ul>
00180  * Command line properties have precedence<p />
00181  * Environment variables are weakest
00182  */
00183 
00184 void 
00185 Property::loadPropertyFile()
00186 {
00187    const string filename = "xmlBlaster.properties";
00188    string path="";
00189    int num=0;
00190 
00191    if (num < 1) {
00192       path = getProperty("propertyFile", false); // command line property
00193       if (!path.empty())
00194          num = readPropertyFile(path, false);
00195    }
00196    if (num < 1) {
00197       path = getProperty("propertyFile", true); // looking in environment as well
00198       if (!path.empty())
00199          num = readPropertyFile(path, false);
00200    }
00201    if (num < 1) {
00202       num = readPropertyFile(filename, false);
00203    }
00204    if (num < 1) {      
00205      path = getStringProperty("user.home", "", true); // Check home directory $HOME
00206      if (!path.empty()) {
00207        num = readPropertyFile(path + FILE_SEP + filename, false);
00208      }
00209    }
00210    if (num < 1) {
00211      if(getenv("XMLBLASTER_HOME")) {
00212        path = getenv("XMLBLASTER_HOME");
00213      if (!path.empty())
00214        num = readPropertyFile(path + FILE_SEP + filename, false);
00215      }
00216    }
00217    if (num > 0) {
00218       replaceVariables(true);
00219    }
00220 }
00221 
00222 
00223 int Property::readPropertyFile(const string &filename, bool overwrite) 
00224 {
00225    ifstream in(filename.c_str());
00226    string  line, tmp;
00227    int     count = 0;
00228    if (in == 0) return -1;
00229    std::cout << "Reading property file " << filename << std::endl;
00230    while (!in.eof()) {
00231       getline(in, tmp);
00232       StringTrim::trimEnd(tmp);
00233       if (tmp.size() > 0 && tmp[tmp.size()-1] == '\\') {
00234          line += tmp.substr(0,tmp.size()-1);
00235          continue;
00236       }
00237       line += tmp;
00238       if (!in.eof()) {
00239          pair<const string, string> valuePair(getPair(line));
00240          if ((valuePair.first != "") && (valuePair.second != "")) {
00241             //std::cout << "readPropertyFile: " << valuePair.first << "=" << valuePair.second << std::endl;
00242             if (setProperty_(valuePair.first, valuePair.second, overwrite))
00243                count++;
00244          }
00245       }
00246       line = "";
00247    }
00248    in.close();
00249 //   if (count > 0)
00250 //      std::cout << "Successfully read " << count << " entries from " << filename << std::endl;
00251    return count;
00252 }
00253 
00254 
00260 int 
00261 Property::writePropertyFile(const char *filename) const 
00262 {
00263    ofstream out(filename);
00264    int      count = 0;
00265    if (out == 0) return count;
00266    MapType::const_iterator iter = properties_.begin();
00267    while (iter != properties_.end()) {
00268      out << (*iter).first << "=" << (*iter).second << std::endl;
00269       iter++;
00270       count++;
00271    }
00272    out.close();
00273    return count;
00274 }
00275 
00283 string 
00284 Property::getProperty(const string &name, bool env) 
00285 {
00286    MapType::const_iterator iter = properties_.find(name);
00287    if (iter == properties_.end()) {
00288       if (!env) return "";
00289       char* envStr = getenv(name.c_str());
00290       if (envStr == 0) return "";
00291       setProperty(name, envStr);
00292       return string(envStr);
00293    }
00294    return (*iter).second;
00295 }
00296 
00297 
00298 bool 
00299 Property::propertyExists(const string &name, bool env) 
00300 {
00301    MapType::const_iterator iter = properties_.find(name);
00302    if (iter == properties_.end()) {
00303       if (!env) return false;
00304       char* envStr = getenv(name.c_str());
00305       if (envStr == 0) return false;
00306       setProperty(name, envStr);
00307    }
00308    return true;
00309 }
00310 
00311 
00312 int 
00313 Property::getIntProperty(const string &name, int def, bool env) 
00314 {
00315    string value = getProperty(name, env);
00316    if (value.length() == 0) return def;
00317    char *test = (char*)0;
00318    int ret = (int)strtol(value.c_str(), &test, 10);
00319    if (test == value.c_str()) return def;
00320 //   int ret = lexical_cast<int>(value);
00321    return ret;
00322 }
00323 
00324 long
00325 Property::getLongProperty(const string &name, long def, bool env)
00326 {
00327    string value = getProperty(name, env);
00328    if (value.empty()) return def;
00329    char *test = (char*)0;
00330    long ret = atol(value.c_str());
00331    if (test == value.c_str()) return def;
00332 //   long ret = lexical_cast<long>(value);
00333    return ret;
00334 }
00335 
00336 Timestamp
00337 Property::getTimestampProperty(const string &name, Timestamp def, bool env)
00338 {
00339    string value = getProperty(name, env);
00340    if (value.length() == 0) return def;
00341    char *test = (char*)0;
00342 //   Timestamp ret = STRING_TO_TIMESTAMP(value.c_str());
00343    if (test == value.c_str()) return def;
00344    Timestamp ret = 0;
00345    try {
00346       lexical_cast<Timestamp>(value);
00347    }
00348    catch (...) {
00349       ret = 0;
00350    }
00351    return ret;
00352 }
00353 
00354 bool
00355 Property::getBoolProperty(const string &name, bool def, bool env) 
00356 {
00357    string value = getProperty(name, env);
00358    return StringTrim::isTrue(value, def);
00359 }
00360 
00361 
00362 string 
00363 Property::getStringProperty(const string &name, const string &def, 
00364                       bool env) 
00365 {
00366    string value = getProperty(name, env);
00367    if (value.length() == 0) return def;
00368    return value;
00369 }
00370 
00371 
00372 // private
00373 bool Property::setProperty_(const string &name, const string &value,
00374                       bool overwrite) 
00375 {
00376    MapType::iterator iter = properties_.find(name);
00377    if (iter != properties_.end()) {
00378       if (overwrite) (*iter).second = value;
00379       else return false;
00380    }
00381    else {
00382       pair<const string, string> valuePair(name, value);
00383       properties_.insert(valuePair);
00384    }
00385    return true;                                                                                                                                         
00386 }
00387 
00388 bool Property::setProperty(const string &name, const string &value,
00389                       bool overwrite) 
00390 {
00391    string newValue = replaceVariable(name, value, true);
00392    bool ret = setProperty_(name, newValue, overwrite);
00393    return ret;
00394 }
00395 
00396 
00401 bool Property::getTypedProperty(const string& name, string& value, bool env)
00402 {
00403    if (!propertyExists(name, env)) return false;
00404    value = getStringProperty(name, "", env);
00405    return true;
00406 }
00407 
00408 bool Property::getTypedProperty(const string& name, int& value, bool env)
00409 {
00410    if (!propertyExists(name, env)) return false;
00411    value = getIntProperty(name, 0, env);
00412    return true;
00413 }
00414 
00415 bool Property::getTypedProperty(const string& name, long& value, bool env)
00416 {
00417    if (!propertyExists(name, env)) return false;
00418    value = getLongProperty(name, 0, env);
00419    return true;
00420 }
00421 
00422 bool Property::getTypedProperty(const string& name, bool& value, bool env)
00423 {
00424    if (!propertyExists(name, env)) return false;
00425    value = getBoolProperty(name, false, env);
00426    return true;
00427 }
00428 
00429 #if __LP64__
00430    // long === long long === 64 bit
00431 #else
00432 bool Property::getTypedProperty(const string& name, Timestamp& value, bool env)
00433 {
00434    if (!propertyExists(name, env)) return false;
00435    value = getTimestampProperty(name, 0, env);
00436    return true;
00437 }
00438 #endif
00439   
00440 std::string Property::toXml(const std::string& extraOffset)
00441 {
00442    string offset = Constants::OFFSET + extraOffset;
00443    string sb;
00444    MapType::const_iterator it;
00445    sb += offset;
00446    sb += "<Property>";
00447    for (it=properties_.begin(); it!=properties_.end(); ++it) {
00448       const string& key = (*it).first;
00449       const string& value = (*it).second;
00450       sb += offset + Constants::INDENT;
00451       sb += "<" + key + ">" + value + "</" + key + ">";
00452    }
00453    sb += offset;
00454    sb += "</Property>";
00455    return sb;
00456 }
00457 
00458 void Property::replaceVariables(bool env) {
00459    MapType::const_iterator it;
00460    for (it=properties_.begin(); it!=properties_.end(); ++it) {
00461       const string& key = (*it).first;
00462       const string& value = (*it).second;
00463       const string newValue = replaceVariable(key, value, env);
00464       if (value != newValue) {
00465          properties_[key] = newValue;
00466       }
00467    }
00468 }
00469 
00470 string Property::replaceVariable(const string &/*key*/, const string &valueOrig, bool env) {
00471    //if (replaceVariables == false)
00472    //   return value;
00473    string value = valueOrig;
00474    string origValue = value;
00475    string tok = "${";
00476    string endTok = "}";
00477    for (int ii = 0;; ii++) {
00478       string::size_type from = value.find(tok);
00479       if (from != string::npos) {
00480          string::size_type to = value.find(endTok, from);
00481          if (to == string::npos) {
00482             //std::cout << "Property.InvalidVariable: Invalid variable '" << value.substr(from) << "', expecting ${} syntax." << std::endl;
00483          }
00484          string sub = value.substr(from, to + endTok.size() - from); // "${XY}"
00485          string subKey = sub.substr(tok.size(), sub.length() - endTok.size() - tok.size()); // "XY"
00486          string subValue = getProperty(subKey, env);
00487          if (subValue.empty()) {
00488             //if (verbose>=2) std::cout << "Property: Unknown variable " << sub << " is not replaced" << std::endl;
00489             return value;
00490          }
00491          value = StringTrim::replaceAll(value, sub, subValue);
00492       }
00493       else {
00494          //if (ii > 0 && verbose>=2) {
00495          //   std::cout << "Property: Replacing '" << key << "=" << origValue << "' to '" << value << "'" << std::endl;
00496          //}
00497          return value;
00498       }
00499       if (ii > MAX_NEST) {
00500          //if (verbose>=1) std::cout << "Property: Maximum nested depth of " << MAX_NEST << " reached for variable '" << getProperty(key, env) << "'." << std::endl;
00501          return value;
00502       }
00503    }
00504 }
00505 
00506 
00507 #endif