1
2 #ifndef _UTIL_PROPERTY_C
3 #define _UTIL_PROPERTY_C
4
5 #include "Property.h"
6 #include <cstdlib> //<stdlib.h>
7 #include <fstream>
8 #include <iostream>
9 #include <util/lexical_cast.h>
10 #include <util/PropertyDef.h>
11 #include <util/Constants.h>
12 #include <util/StringTrim.h>
13
14 using namespace std;
15 using namespace org::xmlBlaster::util;
16
17 #if defined(WINCE) /* No getenv() for Windows CE. TODO: Use some registry workaround as others do */
18 char *getenv(const char *key) {
19 return 0;
20 }
21 #endif
22
23 #define MAX_NEST 50
24
25 Property::Property(int args, const char * const argv[]) : properties_() {
26 initializeDefaultProperties();
27 if (args && argv) {
28 loadCommandLineProps(args, argv, std::string("-"), false); // xmlBlaster-style properties
29 }
30 //loadPropertyFile();
31 }
32
33 Property::Property(MapType propMap) : properties_(propMap)
34 {
35 initializeDefaultProperties();
36 replaceVariables(true);
37 }
38
39 void Property::initializeDefaultProperties()
40 {
41 // Add some predefined variables to be useful in xmlBlaster.properties as ${user.home} etc.
42 bool useEnv = true;
43 bool overwrite = false;
44
45 #if defined(_WINDOWS)
46 // Windows: _WINDOWS
47 // HOMEDRIVE=C:
48 // HOMEPATH=\Documents and Settings\Marcel
49 char *driveP = getenv("HOMEDRIVE");
50 string drive = (driveP != 0) ? string(driveP) : string("");
51 char *pathP = getenv("HOMEPATH");
52 string path = (pathP != 0) ? string(pathP) : string("");
53 setProperty("user.home", drive + path);
54 #else
55 if (!propertyExists("user.home", false)) {
56 string value = getProperty("HOME", useEnv);
57 if (value != "") {
58 setProperty("user.home", value, true); // UNIX
59 }
60 }
61 #endif
62 //std::cout << "user.home=" << getProperty("user.home", "??") << std::endl;
63
64 if (!propertyExists("user.name", false)) {
65 string value = getProperty("USER", useEnv);
66 if (value != "") {
67 setProperty("user.name", value, true); // UNIX
68 }
69 else {
70 // Windows: _WINDOWS
71 // USERNAME=joe
72 char *puser = getenv("USERNAME");
73 if (puser) {
74 string tmp = puser;
75 setProperty("user.name", tmp, true);
76 }
77 else {
78 // HOMEPATH=\Documents and Settings\Marcel
79 char *pathP = getenv("HOMEPATH");
80 string path = (pathP != 0) ? string(pathP) : string("");
81 std::string::size_type pos = path.rfind(FILE_SEP);
82 if (pos != string::npos && pos < path.size()-1) {
83 setProperty("user.name", path.substr(pos+1));
84 }
85 }
86 }
87 }
88
89 if (!propertyExists("java.io.tmpdir", false)) {
90 string value = getProperty("TMP", useEnv);
91 if (value != "") {
92 setProperty("java.io.tmpdir", value, true);
93 }
94 }
95
96 // XMLBLASTER_HOME
97
98 setProperty("file.separator", FILE_SEP, overwrite); // '/' on UNIX
99 setProperty("path.separator", PATH_SEP, overwrite); // ':' on UNIX
100
101 // _WINDOWS:
102 // COMPUTERNAME=myserver
103 // LOGONSERVER=\\myserver
104 // NUMBER_OF_PROCESSORS=1
105 // OS=Windows_NT
106 // PROCESSOR_ARCHITECTURE=x86
107 // PROCESSOR_IDENTIFIER=x86 Family 15 Model 2 Stepping 4, GenuineIntel
108 // PROCESSOR_LEVEL=15
109 // PROCESSOR_REVISION=0204
110 // USERDOMAIN=myserver
111 // USERNAME=joe
112 // USERPROFILE=C:\Documents and Settings\marcel
113 // VC7=1
114 // HOMEDRIVE=C:
115 // HOMEPATH=\Documents and Settings\marcel
116 // TMP=C:\DOCUME~1\marcel\LOCALS~1\Temp
117
118 // os.name = Linux
119 // os.name = "Windows XP" os.version=5.1 (_WINDOWS)
120 // line.separator = CRLF ...
121 // java.io.tmpdir = /tmp ... C:\DOCUME~1\marcel\LOCALS~1\Temp\ == C:\Documents and Settings\marcel\Local Settings\Temp (_WINDOWS)
122 // user.dir = /home/xmlblast/test -> getcwd()
123 // sun.cpu.endian = little
124 }
125
126 /**
127 * @see Property.h#loadCommandLineProps
128 */
129 int
130 Property::loadCommandLineProps(int args,
131 const char * const argv[],
132 const string &prefix,
133 bool javaStyle)
134 {
135
136 int count = 1, ret=0, nmax = args;
137 string value;
138 //if (!javaStyle) nmax--; // they come in separated pairs
139 while (count < nmax) {
140 string name = argv[count];
141 if (name.find(prefix) == 0) { // it is a property
142 name = name.substr(prefix.length()); // remove separator e.g. "-trace" -> "trace"
143 if (!javaStyle) { // Corba style (or other non-java styles)
144 //if (name == "h" || name == "help" || name == "?" ) {
145 value = (count < nmax-1) ? argv[count+1] : "";
146 if (value == ""/* || value.find(prefix) == 0*/) { // A property without a value -> we set it to true, for example --help
147 if (setProperty_(name, lexical_cast<std::string>(true))) ret++;
148 }
149 else {
150 count++;
151 //std::cout << "readPropertyCommandLine: " << name << "=" << value << std::endl;
152 if (setProperty_(name, value)) ret++;
153 }
154 }
155 else { // java style: prop1=val1
156 pair<const string, string> propPair(getPair(name));
157 if (setProperty_(propPair.first, propPair.second)) ret++;
158 }
159 }
160 count++;
161 }
162 // if (count > 0)
163 // std::cout << "Successfully read " << (count-1)/2 << " command line arguments" << std::endl;
164 if (ret > 0) {
165 replaceVariables(true);
166 }
167 return ret;
168 }
169
170
171 /*
172 * xmlBlaster.properties is searched in this sequence:
173 * <ul>
174 * <li>the command line parameter '-propertyFile', e.g. "-propertyFile /tmp/xy.properties"</li>
175 * <li>the environment variable 'propertyFile', e.g. "propertyFile=/tmp/xy.properties"</li>
176 * <li>the local directory: ./xmlBlaster.properties</li>
177 * <li>in your home directory, HOME/xmlBlaster.properties</li>
178 * <li>in the $XMLBLASTER_HOME directory</li>
179 * </ul>
180 * Command line properties have precedence<p />
181 * Environment variables are weakest
182 */
183
184 void
185 Property::loadPropertyFile()
186 {
187 const string filename = "xmlBlaster.properties";
188 string path="";
189 int num=0;
190
191 if (num < 1) {
192 path = getProperty("propertyFile", false); // command line property
193 if (!path.empty())
194 num = readPropertyFile(path, false);
195 }
196 if (num < 1) {
197 path = getProperty("propertyFile", true); // looking in environment as well
198 if (!path.empty())
199 num = readPropertyFile(path, false);
200 }
201 if (num < 1) {
202 num = readPropertyFile(filename, false);
203 }
204 if (num < 1) {
205 path = getStringProperty("user.home", "", true); // Check home directory $HOME
206 if (!path.empty()) {
207 num = readPropertyFile(path + FILE_SEP + filename, false);
208 }
209 }
210 if (num < 1) {
211 if(getenv("XMLBLASTER_HOME")) {
212 path = getenv("XMLBLASTER_HOME");
213 if (!path.empty())
214 num = readPropertyFile(path + FILE_SEP + filename, false);
215 }
216 }
217 if (num > 0) {
218 replaceVariables(true);
219 }
220 }
221
222
223 int Property::readPropertyFile(const string &filename, bool overwrite)
224 {
225 ifstream in(filename.c_str());
226 string line, tmp;
227 int count = 0;
228 if (in == 0) return -1;
229 std::cout << "Reading property file " << filename << std::endl;
230 while (!in.eof()) {
231 getline(in, tmp);
232 StringTrim::trimEnd(tmp);
233 if (tmp.size() > 0 && tmp[tmp.size()-1] == '\\') {
234 line += tmp.substr(0,tmp.size()-1);
235 continue;
236 }
237 line += tmp;
238 if (!in.eof()) {
239 pair<const string, string> valuePair(getPair(line));
240 if ((valuePair.first != "") && (valuePair.second != "")) {
241 //std::cout << "readPropertyFile: " << valuePair.first << "=" << valuePair.second << std::endl;
242 if (setProperty_(valuePair.first, valuePair.second, overwrite))
243 count++;
244 }
245 }
246 line = "";
247 }
248 in.close();
249 // if (count > 0)
250 // std::cout << "Successfully read " << count << " entries from " << filename << std::endl;
251 return count;
252 }
253
254
255 /**
256 * writes the properties to a file specified in the argument list. If it
257 * could not write to the file, a zero is returned.
258 * Returns the number of properties written to the file.
259 */
260 int
261 Property::writePropertyFile(const char *filename) const
262 {
263 ofstream out(filename);
264 int count = 0;
265 if (out == 0) return count;
266 MapType::const_iterator iter = properties_.begin();
267 while (iter != properties_.end()) {
268 out << (*iter).first << "=" << (*iter).second << std::endl;
269 iter++;
270 count++;
271 }
272 out.close();
273 return count;
274 }
275
276 /**
277 * Gets the property with the specified name. If no such property exists,
278 * an empty string is returned. If the string is not found, it searches
279 * in among the environment variables (only if env is set to true which
280 * is the default). In the property is not found there either, it returns
281 * an empty string.
282 */
283 string
284 Property::getProperty(const string &name, bool env)
285 {
286 MapType::const_iterator iter = properties_.find(name);
287 if (iter == properties_.end()) {
288 if (!env) return "";
289 char* envStr = getenv(name.c_str());
290 if (envStr == 0) return "";
291 setProperty(name, envStr);
292 return string(envStr);
293 }
294 return (*iter).second;
295 }
296
297
298 bool
299 Property::propertyExists(const string &name, bool env)
300 {
301 MapType::const_iterator iter = properties_.find(name);
302 if (iter == properties_.end()) {
303 if (!env) return false;
304 char* envStr = getenv(name.c_str());
305 if (envStr == 0) return false;
306 setProperty(name, envStr);
307 }
308 return true;
309 }
310
311
312 int
313 Property::getIntProperty(const string &name, int def, bool env)
314 {
315 string value = getProperty(name, env);
316 if (value.length() == 0) return def;
317 char *test = (char*)0;
318 int ret = (int)strtol(value.c_str(), &test, 10);
319 if (test == value.c_str()) return def;
320 // int ret = lexical_cast<int>(value);
321 return ret;
322 }
323
324 long
325 Property::getLongProperty(const string &name, long def, bool env)
326 {
327 string value = getProperty(name, env);
328 if (value.empty()) return def;
329 char *test = (char*)0;
330 long ret = atol(value.c_str());
331 if (test == value.c_str()) return def;
332 // long ret = lexical_cast<long>(value);
333 return ret;
334 }
335
336 Timestamp
337 Property::getTimestampProperty(const string &name, Timestamp def, bool env)
338 {
339 string value = getProperty(name, env);
340 if (value.length() == 0) return def;
341 char *test = (char*)0;
342 // Timestamp ret = STRING_TO_TIMESTAMP(value.c_str());
343 if (test == value.c_str()) return def;
344 Timestamp ret = 0;
345 try {
346 lexical_cast<Timestamp>(value);
347 }
348 catch (...) {
349 ret = 0;
350 }
351 return ret;
352 }
353
354 bool
355 Property::getBoolProperty(const string &name, bool def, bool env)
356 {
357 string value = getProperty(name, env);
358 return StringTrim::isTrue(value, def);
359 }
360
361
362 string
363 Property::getStringProperty(const string &name, const string &def,
364 bool env)
365 {
366 string value = getProperty(name, env);
367 if (value.length() == 0) return def;
368 return value;
369 }
370
371
372 // private
373 bool Property::setProperty_(const string &name, const string &value,
374 bool overwrite)
375 {
376 MapType::iterator iter = properties_.find(name);
377 if (iter != properties_.end()) {
378 if (overwrite) (*iter).second = value;
379 else return false;
380 }
381 else {
382 pair<const string, string> valuePair(name, value);
383 properties_.insert(valuePair);
384 }
385 return true;
386 }
387
388 bool Property::setProperty(const string &name, const string &value,
389 bool overwrite)
390 {
391 string newValue = replaceVariable(name, value, true);
392 bool ret = setProperty_(name, newValue, overwrite);
393 return ret;
394 }
395
396
397 /**
398 * To allow templatized getting of properties. It returns true if the property has been found. In that
399 * case, the return value is put into the 'value' argument.
400 */
401 bool Property::getTypedProperty(const string& name, string& value, bool env)
402 {
403 if (!propertyExists(name, env)) return false;
404 value = getStringProperty(name, "", env);
405 return true;
406 }
407
408 bool Property::getTypedProperty(const string& name, int& value, bool env)
409 {
410 if (!propertyExists(name, env)) return false;
411 value = getIntProperty(name, 0, env);
412 return true;
413 }
414
415 bool Property::getTypedProperty(const string& name, long& value, bool env)
416 {
417 if (!propertyExists(name, env)) return false;
418 value = getLongProperty(name, 0, env);
419 return true;
420 }
421
422 bool Property::getTypedProperty(const string& name, bool& value, bool env)
423 {
424 if (!propertyExists(name, env)) return false;
425 value = getBoolProperty(name, false, env);
426 return true;
427 }
428
429 #if __LP64__
430 // long === long long === 64 bit
431 #else
432 bool Property::getTypedProperty(const string& name, Timestamp& value, bool env)
433 {
434 if (!propertyExists(name, env)) return false;
435 value = getTimestampProperty(name, 0, env);
436 return true;
437 }
438 #endif
439
440 std::string Property::toXml(const std::string& extraOffset)
441 {
442 string offset = Constants::OFFSET + extraOffset;
443 string sb;
444 MapType::const_iterator it;
445 sb += offset;
446 sb += "<Property>";
447 for (it=properties_.begin(); it!=properties_.end(); ++it) {
448 const string& key = (*it).first;
449 const string& value = (*it).second;
450 sb += offset + Constants::INDENT;
451 sb += "<" + key + ">" + value + "</" + key + ">";
452 }
453 sb += offset;
454 sb += "</Property>";
455 return sb;
456 }
457
458 void Property::replaceVariables(bool env) {
459 MapType::const_iterator it;
460 for (it=properties_.begin(); it!=properties_.end(); ++it) {
461 const string& key = (*it).first;
462 const string& value = (*it).second;
463 const string newValue = replaceVariable(key, value, env);
464 if (value != newValue) {
465 properties_[key] = newValue;
466 }
467 }
468 }
469
470 string Property::replaceVariable(const string &/*key*/, const string &valueOrig, bool env) {
471 //if (replaceVariables == false)
472 // return value;
473 string value = valueOrig;
474 string origValue = value;
475 string tok = "${";
476 string endTok = "}";
477 for (int ii = 0;; ii++) {
478 string::size_type from = value.find(tok);
479 if (from != string::npos) {
480 string::size_type to = value.find(endTok, from);
481 if (to == string::npos) {
482 //std::cout << "Property.InvalidVariable: Invalid variable '" << value.substr(from) << "', expecting ${} syntax." << std::endl;
483 }
484 string sub = value.substr(from, to + endTok.size() - from); // "${XY}"
485 string subKey = sub.substr(tok.size(), sub.length() - endTok.size() - tok.size()); // "XY"
486 string subValue = getProperty(subKey, env);
487 if (subValue.empty()) {
488 //if (verbose>=2) std::cout << "Property: Unknown variable " << sub << " is not replaced" << std::endl;
489 return value;
490 }
491 value = StringTrim::replaceAll(value, sub, subValue);
492 }
493 else {
494 //if (ii > 0 && verbose>=2) {
495 // std::cout << "Property: Replacing '" << key << "=" << origValue << "' to '" << value << "'" << std::endl;
496 //}
497 return value;
498 }
499 if (ii > MAX_NEST) {
500 //if (verbose>=1) std::cout << "Property: Maximum nested depth of " << MAX_NEST << " reached for variable '" << getProperty(key, env) << "'." << std::endl;
501 return value;
502 }
503 }
504 }
505
506
507 #endif
syntax highlighted by Code2HTML, v. 0.9.1