/* -*-c++-*- *//* osgEarth - Geospatial SDK for OpenSceneGraph
* Copyright 2019 Pelican Mapping
* http://osgearth.org
*
* osgEarth is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/#ifndef OSGEARTH_STRING_UTILS_H#define OSGEARTH_STRING_UTILS_H 1#include<osgEarth/Common>#include<osg/Vec3>#include<osg/Vec3d>#include<osg/Vec4>#include<osg/Vec4ub>#include<string>#include<algorithm>#include<vector>#include<sstream>#include<locale>#include<iomanip>#include<map>#include<set>#include<ctype.h>namespace osgEarth
{extern OSGEARTH_EXPORT const std::string EMPTY_STRING;typedef std::vector<std::string> StringVector;typedef std::set<std::string> StringSet;/** Replaces all the instances of "pattern" with "replacement" in "in_out" */extern OSGEARTH_EXPORT std::string&replaceIn(
std::string& in_out,const std::string& pattern,const std::string& replacement );/** Replaces all the instances of "pattern" with "replacement" in "in_out" (case-insensitive) */extern OSGEARTH_EXPORT std::string&ciReplaceIn(
std::string& in_out,const std::string& pattern,const std::string& replacement );/**
* Trims whitespace from the ends of a string.
*/extern OSGEARTH_EXPORT std::string trim(const std::string& in );/**
* Trims whitespace from the ends of a string; in-place modification on the string to reduce string copies.
*/extern OSGEARTH_EXPORT voidtrim2( std::string& str );//! Removes leading and trailing whitespace, and replaces all other//! whitespace with single spacesextern OSGEARTH_EXPORT std::string trimAndCompress(const std::string& in);/**
* True is "ref" starts with "pattern"
*/extern OSGEARTH_EXPORT boolstartsWith(const std::string& ref,const std::string& pattern,bool caseSensitive =true,const std::locale& locale =std::locale());/**
* True is "ref" ends with "pattern"
*/extern OSGEARTH_EXPORT boolendsWith(const std::string& ref,const std::string& pattern,bool caseSensitive =true,const std::locale& locale =std::locale());/**
* Case-insensitive compare
*/extern OSGEARTH_EXPORT boolciEquals(const std::string& lhs,const std::string& rhs,const std::locale& local = std::locale());/**
* Case-insensitive STL comparator
*/struct OSGEARTH_EXPORT CIStringComp {booloperator()(const std::string& lhs,const std::string& rhs)const;};extern OSGEARTH_EXPORT std::string joinStrings(const StringVector& input,char delim );/** Returns a lower-case version of the input string. */extern OSGEARTH_EXPORT std::string toLower(const std::string& input );/** Parses a color string in the form "255 255 255 255" (r g b a [0..255]) into an OSG color. */extern OSGEARTH_EXPORT osg::Vec4ub stringToColor(const std::string& str, osg::Vec4ub default_value);/** Creates a string in the form "255 255 255 255" (r g b a [0..255]) from a color */extern OSGEARTH_EXPORT std::string colorToString(const osg::Vec4ub& c );/** Converts a string to a vec3f */extern OSGEARTH_EXPORT osg::Vec3f stringToVec3f(const std::string& str,const osg::Vec3f& default_value );/** Converts a vec3f to a string */extern OSGEARTH_EXPORT std::string vec3fToString(const osg::Vec3f& v );/** Parses an HTML color ("#rrggbb" or "#rrggbbaa") into an OSG color. */extern OSGEARTH_EXPORT osg::Vec4f htmlColorToVec4f(const std::string& html );/** Makes an HTML color ("#rrggbb" or "#rrggbbaa") from an OSG color. */extern OSGEARTH_EXPORT std::string vec4fToHtmlColor(const osg::Vec4f& c );/** Makes a valid filename out of a string */extern OSGEARTH_EXPORT std::string toLegalFileName(const std::string& input,bool allowSubdir=false);/** Generates a hashed integer for a string (poor man's MD5) */extern OSGEARTH_EXPORT unsignedhashString(const std::string& input );/** Same as hashString but returns a string value. */extern OSGEARTH_EXPORT std::string hashToString(const std::string& input);/**
* Gets the total number of seconds formatted as H:M:S
*/extern OSGEARTH_EXPORT std::string prettyPrintTime(double seconds );/**
* Gets a pretty printed version of the given size in MB.
*/extern OSGEARTH_EXPORT std::string prettyPrintSize(double mb );//------------------------------------------------------------------------// conversion templates// converts a string to primitive using serializationtemplate<typename T>inline T
as(const std::string& str,const T& default_value ){
T temp = default_value;
std::istringstream strin( str );if(!strin.eof()) strin >> temp;return temp;}// template specialization for integers (to handle hex)#define AS_INT_DEC_OR_HEX(TYPE) \
template<> inline TYPE \
as< TYPE >(const std::string& str, const TYPE & dv) { \
TYPE temp = dv; \
std::istringstream strin( trim(str) ); \
if ( !strin.eof() ) { \
if ( str.length() >= 2 && str[0] == '0' && str[1] == 'x' ) { \
strin.seekg( 2 ); \
strin >> std::hex >> temp; \
} \
else { \
strin >> temp; \
} \
} \
return temp; \
}AS_INT_DEC_OR_HEX(int)AS_INT_DEC_OR_HEX(unsigned)AS_INT_DEC_OR_HEX(short)AS_INT_DEC_OR_HEX(unsignedshort)AS_INT_DEC_OR_HEX(long)AS_INT_DEC_OR_HEX(unsignedlong)// template specialization for a booltemplate<>inlinebool
as<bool>(const std::string& str,constbool& default_value ){
std::string temp =toLower(str);return
temp =="true"|| temp =="yes"|| temp =="on"?true:
temp =="false"|| temp =="no"|| temp =="off"?false:
default_value;}template<>inline osg::Vec3f
as<osg::Vec3f>(const std::string& str,const osg::Vec3f& default_value ){returnstringToVec3f(str, default_value);}// template specialization for stringtemplate<>inline std::string
as<std::string>(const std::string& str,const std::string& default_value ){return str;}// snips a substring and parses it.template<typename T>inlineboolas(const std::string& in,unsigned start,unsigned len, T default_value){
std::string buf;
std::copy( in.begin()+start, in.begin()+start+len, std::back_inserter(buf));return as<T>(buf, default_value);}// converts a primitive to a string// TODO: precision??template<typename T>inline std::string
toString(const T& value){
std::stringstream out;//out << std::setprecision(20) << std::fixed << value;
out << std::setprecision(20)<< value;
std::string outStr;
outStr = out.str();return outStr;}// template speciallization for a bool to print out "true" or "false"template<>inline std::string
toString<bool>(constbool& value){return value ?"true":"false";}template<>inline std::string
toString<osg::Vec3f>(const osg::Vec3f& value){returnvec3fToString(value);}/**
* Assembles and returns an inline string using a stream-like << operator.
* Example:
* std::string str = Stringify() << "Hello, world " << variable;
*/struct Stringify
{operator std::string ()const{
std::string result;
result = buf.str();return result;}template<typename T>
Stringify&operator<<(const T& val){ buf << val;return(*this);}
Stringify&operator<<(const Stringify& val){ buf <<(std::string)val;return(*this);}protected:
std::stringstream buf;};template<>inline
Stringify& Stringify::operator<<<bool>(constbool& val){ buf <<(val ?"true":"false");return(*this);}template<>inline
Stringify& Stringify::operator<<<osg::Vec3f>(const osg::Vec3f& val){
buf << val.x()<<" "<< val.y()<<" "<< val.z();return(*this);}template<>inline
Stringify& Stringify::operator<<<osg::Vec3d>(const osg::Vec3d& val ){
buf << val.x()<<" "<< val.y()<<" "<< val.z();return(*this);}template<>inline
Stringify& Stringify::operator<<<osg::Vec4f>(const osg::Vec4f& val){
buf << val.r()<<" "<< val.g()<<" "<< val.b()<<" "<< val.a();return(*this);}/**
* Splits a string up into a vector of strings based on a set of
* delimiters, quotes, and rules.
*/classOSGEARTH_EXPORT StringTokenizer
{public:StringTokenizer(const std::string& delims =" \t\r\n",const std::string& quotes ="'\"");StringTokenizer(const std::string& input, StringVector& output,const std::string& delims =" \t\r\n",const std::string& quotes ="'\"",bool keepEmpties =true,bool trimTokens =true);voidtokenize(const std::string& input, StringVector& output )const;bool&keepEmpties(){return _allowEmpties;}bool&trimTokens(){return _trimTokens;}voidaddDelim(char delim,bool keepAsToken =false);voidaddDelims(const std::string& delims,bool keepAsTokens =false);voidaddQuote(char delim,bool keepInToken =false);voidaddQuotes(const std::string& delims,bool keepInTokens =false);private:typedef std::map<char,bool> TokenMap;
TokenMap _delims;
TokenMap _quotes;bool _allowEmpties;bool _trimTokens;};}#endif// OSGEARTH_STRING_UTILS_H
/* -*-c++-*- *//* osgEarth - Geospatial SDK for OpenSceneGraph
* Copyright 2019 Pelican Mapping
* http://osgearth.org
*
* osgEarth is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/#include<osgEarth/StringUtils>#include<cctype>#include<cstring>usingnamespace osgEarth;
StringTokenizer::StringTokenizer(const std::string& delims,const std::string& quotes ):_allowEmpties(true),
_trimTokens (true){addDelims( delims );addQuotes( quotes );}
StringTokenizer::StringTokenizer(const std::string& input,
StringVector& output,const std::string& delims,const std::string& quotes,bool allowEmpties,bool trimTokens ):_allowEmpties( allowEmpties ),
_trimTokens ( trimTokens ){addDelims( delims );addQuotes( quotes );tokenize( input, output );}void
StringTokenizer::addDelim(char delim,bool keep ){
_delims[delim]= keep;}void
StringTokenizer::addDelims(const std::string& delims,bool keep ){for(unsigned i=0; i<delims.size();++i )addDelim( delims[i], keep );}void
StringTokenizer::addQuote(char quote,bool keep ){
_quotes[quote]= keep;}void
StringTokenizer::addQuotes(const std::string& quotes,bool keep ){for(unsigned i=0; i<quotes.size();++i )addQuote( quotes[i], keep );}void
StringTokenizer::tokenize(const std::string& input, StringVector& output )const{
output.clear();
std::stringstream buf;bool quoted =false;char lastQuoteChar ='\0';for( std::string::const_iterator i = input.begin(); i != input.end();++i ){char c =*i;
TokenMap::const_iterator q = _quotes.find( c );if( quoted ){if( q != _quotes.end()&& lastQuoteChar == c){
quoted =false;
lastQuoteChar ='\0';if( q->second )
buf << c;}else{
buf << c;}}else{if( q != _quotes.end()){
quoted =true;
lastQuoteChar = c;if( q->second )
buf << c;}else{
TokenMap::const_iterator d = _delims.find( c );if( d == _delims.end()){
buf << c;}else{
std::string token = buf.str();if( _trimTokens )trim2( token );if( _allowEmpties ||!token.empty())
output.push_back( token );if( d->second ==true){
output.push_back( std::string(1, c));}
buf.str("");}}}}
std::string bufstr = buf.str();if( _trimTokens )trim2( bufstr );if(!bufstr.empty())
output.push_back( bufstr );}//--------------------------------------------------------------------------const std::string osgEarth::EMPTY_STRING;
std::string
osgEarth::toLegalFileName(const std::string& input,bool allowSubdirs){// See: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_282// We omit '-' so we can use it for the HEX identifier.staticconst std::string legalWithoutSubdirs("ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvwxyz0123456789_.");staticconst std::string legalWithDirs ("ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvwxyz0123456789_./");
std::size_t pos = input.find("://");
pos = pos == std::string::npos ?0: pos+3;const std::string& legal = allowSubdirs? legalWithDirs : legalWithoutSubdirs;
std::stringstream buf;for(; pos < input.size();++pos ){
std::string::const_reference c = input[pos];if(legal.find(c)!= std::string::npos)
buf << c;else
buf <<"-"<< std::hex <<static_cast<unsigned>(c)<<"-";}
std::string result;
result = buf.str();return result;}/** MurmurHash 2.0 (http://sites.google.com/site/murmurhash/) */unsigned
osgEarth::hashString(const std::string& input ){constunsignedint m =0x5bd1e995;constint r =24;unsignedint len = input.length();constchar* data = input.c_str();unsignedint h = m ^ len;// using "m" as the seed.while(len >=4){unsignedint k =*(unsignedint*)data;
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
data +=4;
len -=4;}switch(len){case3: h ^= data[2]<<16;case2: h ^= data[1]<<8;case1: h ^= data[0];
h *= m;};
h ^= h >>13;
h *= m;
h ^= h >>15;return h;}
std::string
osgEarth::hashToString(const std::string& input){returnStringify()<< std::hex << std::setw(8)<< std::setfill('0')<<hashString(input);}/** Parses an HTML color ("#rrggbb" or "#rrggbbaa") into an OSG color. */
osg::Vec4f
osgEarth::htmlColorToVec4f(const std::string& html ){
std::string t = osgEarth::toLower(html);
osg::Vec4ub c(0,0,0,255);if( t.length()>=7){
c.r()|= t[1]<='9'?(t[1]-'0')<<4:(10+(t[1]-'a'))<<4;
c.r()|= t[2]<='9'?(t[2]-'0'):(10+(t[2]-'a'));
c.g()|= t[3]<='9'?(t[3]-'0')<<4:(10+(t[3]-'a'))<<4;
c.g()|= t[4]<='9'?(t[4]-'0'):(10+(t[4]-'a'));
c.b()|= t[5]<='9'?(t[5]-'0')<<4:(10+(t[5]-'a'))<<4;
c.b()|= t[6]<='9'?(t[6]-'0'):(10+(t[6]-'a'));if( t.length()==9){
c.a()=0;
c.a()|= t[7]<='9'?(t[7]-'0')<<4:(10+(t[7]-'a'))<<4;
c.a()|= t[8]<='9'?(t[8]-'0'):(10+(t[8]-'a'));}}return osg::Vec4f(((float)c.r())/255.0f,((float)c.g())/255.0f,((float)c.b())/255.0f,((float)c.a())/255.0f);}/** Makes an HTML color ("#rrggbb" or "#rrggbbaa") from an OSG color. */
std::string
osgEarth::vec4fToHtmlColor(const osg::Vec4f& c ){
std::stringstream buf;
buf <<"#";
buf << std::hex << std::setw(2)<< std::setfill('0')<<(int)(c.r()*255.0f);
buf << std::hex << std::setw(2)<< std::setfill('0')<<(int)(c.g()*255.0f);
buf << std::hex << std::setw(2)<< std::setfill('0')<<(int)(c.b()*255.0f);if( c.a()<1.0f)
buf << std::hex << std::setw(2)<< std::setfill('0')<<(int)(c.a()*255.0f);
std::string ssStr;
ssStr = buf.str();return ssStr;}/** Parses a color string in the form "255 255 255 255" (r g b a [0..255]) into an OSG color. */
osg::Vec4ub
osgEarth::stringToColor(const std::string& str, osg::Vec4ub default_value){
osg::Vec4ub color = default_value;
std::istringstream strin(str);int r, g, b, a;if(strin >> r && strin >> g && strin >> b && strin >> a){
color.r()=(unsignedchar)r;
color.g()=(unsignedchar)g;
color.b()=(unsignedchar)b;
color.a()=(unsignedchar)a;}return color;}/** Creates a string in the form "255 255 255 255" (r g b a [0..255]) from a color */
std::string
osgEarth::colorToString(const osg::Vec4ub& c ){
std::stringstream ss;
ss <<(int)c.r()<<" "<<(int)c.g()<<" "<<(int)c.b()<<" "<<(int)c.a();
std::string ssStr;
ssStr = ss.str();return ssStr;}/** Converts a string to a vec3f */
osg::Vec3f
osgEarth::stringToVec3f(const std::string& str,const osg::Vec3f& default_value ){
std::stringstream buf(str);
osg::Vec3f out = default_value;
buf >> out.x();if(!buf.eof()){
buf >> out.y()>> out.z();}else{
out.y()= out.x();
out.z()= out.x();}return out;}/** Converts a vec3f to a string */
std::string
osgEarth::vec3fToString(const osg::Vec3f& v ){
std::stringstream buf;
buf << std::setprecision(6)<< v.x()<<" "<< v.y()<<" "<< v.z()<< std::endl;
std::string result;
result = buf.str();return result;}/** Replaces all the instances of "sub" with "other" in "s". */
std::string&
osgEarth::replaceIn( std::string& s,const std::string& sub,const std::string& other){if( sub.empty())return s;
size_t b=0;for(;;){
b = s.find( sub, b );if( b == s.npos )break;
s.replace( b, sub.size(), other );
b += other.size();}return s;}
std::string&
osgEarth::ciReplaceIn( std::string& s,const std::string& pattern,const std::string& replacement ){if( pattern.empty())return s;
std::string upperSource = s;
std::transform( upperSource.begin(), upperSource.end(), upperSource.begin(),(int(*)(int))std::toupper );
std::string upperPattern = pattern;
std::transform( upperPattern.begin(), upperPattern.end(), upperPattern.begin(),(int(*)(int))std::toupper );for( size_t b =0;;){
b = upperSource.find( upperPattern, b );if( b == s.npos )break;
s.replace( b, pattern.size(), replacement );
upperSource.replace( b, upperPattern.size(), replacement );
b += replacement.size();}return s;}/**
* Trims whitespace from the ends of a string.
* by Rodrigo C F Dias
* http://www.codeproject.com/KB/stl/stdstringtrim.aspx
*/void
osgEarth::trim2( std::string& str ){staticconst std::string whitespace (" \t\f\v\n\r");
std::string::size_type pos = str.find_last_not_of( whitespace );if(pos != std::string::npos){
str.erase(pos +1);
pos = str.find_first_not_of( whitespace );if(pos != std::string::npos) str.erase(0, pos);}else str.erase(str.begin(), str.end());}/**
* Trims whitespace from the ends of a string, returning a
* copy of the string with whitespace removed.
*/
std::string
osgEarth::trim(const std::string& in ){
std::string str = in;trim2( str );return str;}
std::string
osgEarth::trimAndCompress(const std::string& in){bool inwhite =true;
std::stringstream buf;for(unsigned i =0; i < in.length();++i){char c = in[i];if(::isspace(c)){if(!inwhite){
buf <<' ';
inwhite =true;}}else{
inwhite =false;
buf << c;}}
std::string r;
r = buf.str();trim2(r);return r;}
std::string
osgEarth::joinStrings(const StringVector& input,char delim ){
std::stringstream buf;for( StringVector::const_iterator i = input.begin(); i != input.end();++i ){
buf <<*i;if((i+1)!= input.end()) buf << delim;}
std::string result;
result = buf.str();return result;}/** Returns a lower-case version of the input string. */
std::string
osgEarth::toLower(const std::string& input ){
std::string output = input;
std::transform( output.begin(), output.end(), output.begin(),::tolower );return output;}
std::string
osgEarth::prettyPrintTime(double seconds ){int hours =(int)floor(seconds /(3600.0));
seconds -= hours *3600.0;int minutes =(int)floor(seconds/60.0);
seconds -= minutes *60.0;
std::stringstream buf;
buf << hours <<":"<< minutes <<":"<< seconds;return buf.str();}
std::string
osgEarth::prettyPrintSize(double mb ){
std::stringstream buf;// Convert to terabytesif( mb >1024*1024){
buf <<(mb /(1024.0*1024.0))<<" TB";}elseif(mb >1024){
buf <<(mb /1024.0)<<" GB";}else{
buf << mb <<" MB";}return buf.str();}namespace{template<typename charT>struct ci_equal {ci_equal(const std::locale& loc ):_loc(loc){}booloperator()(charT c1, charT c2){return std::toupper(c1,_loc)== std::toupper(c2,_loc);}const std::locale& _loc;};}bool
osgEarth::ciEquals(const std::string& lhs,const std::string& rhs,const std::locale& loc ){if( lhs.length()!= rhs.length())returnfalse;for(unsigned i=0; i<lhs.length();++i ){if( std::toupper(lhs[i], loc)!= std::toupper(rhs[i], loc))returnfalse;}returntrue;}#if defined(WIN32) && !defined(__CYGWIN__)# define STRICMP ::stricmp#else# define STRICMP ::strcasecmp#endifbool CIStringComp::operator()(const std::string& lhs,const std::string& rhs)const{returnSTRICMP( lhs.c_str(), rhs.c_str())<0;}bool
osgEarth::startsWith(const std::string& ref,const std::string& pattern,bool caseSensitive,const std::locale& loc ){if( pattern.length()> ref.length())returnfalse;if( caseSensitive ){for(unsigned i=0; i<pattern.length();++i ){if( ref[i]!= pattern[i])returnfalse;}}else{for(unsigned i=0; i<pattern.length();++i ){if( std::toupper(ref[i], loc)!= std::toupper(pattern[i],loc))returnfalse;}}returntrue;}bool
osgEarth::endsWith(const std::string& ref,const std::string& pattern,bool caseSensitive,const std::locale& loc ){if( pattern.length()> ref.length())returnfalse;unsigned offset = ref.size()-pattern.length();if( caseSensitive ){for(unsigned i=0; i < pattern.length();++i ){if( ref[i+offset]!= pattern[i])returnfalse;}}else{for(unsigned i=0; i < pattern.length();++i ){if( std::toupper(ref[i+offset], loc)!= std::toupper(pattern[i],loc))returnfalse;}}returntrue;}