File indexing completed on 2024-04-21 04:47:53
0001 /**************************************************************************************** 0002 * Copyright (c) 2004 Shintaro Matsuoka <shin@shoegazed.org> * 0003 * Copyright (c) 2006 Martin Aumueller <aumuell@reserv.at> * 0004 * Copyright (c) 2011 Sergey Ivanov <123kash@gmail.com> * 0005 * * 0006 * This program is free software; you can redistribute it and/or modify it under * 0007 * the terms of the GNU General Public License as published by the Free Software * 0008 * Foundation; either version 2 of the License, or (at your option) any later * 0009 * version. * 0010 * * 0011 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0012 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0013 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License along with * 0016 * this program. If not, see <http://www.gnu.org/licenses/>. * 0017 ****************************************************************************************/ 0018 0019 #include "QStringx.h" 0020 0021 Amarok::QStringx::QStringx() 0022 { 0023 } 0024 0025 Amarok::QStringx::QStringx( QChar ch ) 0026 : QString( ch ) 0027 { 0028 } 0029 0030 Amarok::QStringx::QStringx( const QString &s ) 0031 : QString( s ) 0032 { 0033 } 0034 0035 Amarok::QStringx::QStringx( const QByteArray &ba ) 0036 : QString( ba ) 0037 { 0038 } 0039 0040 Amarok::QStringx::QStringx( const QChar *unicode, uint length ) 0041 : QString( unicode, length ) 0042 { 0043 } 0044 0045 Amarok::QStringx::QStringx( const char *str ) 0046 : QString( str ) 0047 { 0048 } 0049 0050 Amarok::QStringx::~QStringx() 0051 { 0052 } 0053 0054 QString 0055 Amarok::QStringx::args( const QStringList &args ) const 0056 { 0057 const QStringList text = (*this).split( QRegExp( "%\\d+" ), Qt::KeepEmptyParts ); 0058 0059 QList<QString>::ConstIterator itrText = text.constBegin(); 0060 QList<QString>::ConstIterator itrArgs = args.constBegin(); 0061 QList<QString>::ConstIterator endText = text.constEnd(); 0062 QList<QString>::ConstIterator endArgs = args.constEnd(); 0063 QString merged = (*itrText); 0064 ++itrText; 0065 while( itrText != endText && itrArgs != endArgs ) 0066 { 0067 merged += (*itrArgs) + (*itrText); 0068 ++itrText; 0069 ++itrArgs; 0070 } 0071 0072 Q_ASSERT( itrText == text.end() || itrArgs == args.end() ); 0073 0074 return merged; 0075 } 0076 0077 QString 0078 Amarok::QStringx::namedArgs( const QMap<QString, QString> &args, bool opt ) const 0079 { 0080 // Screen all kinds of brackets and format string with namedOptArgs. 0081 QString formatString = *this; 0082 formatString.replace( QRegExp( "([\\[\\]{}])" ),"\\\\1" ); 0083 0084 // Legacy code returned empty string if any token was empty, so do the same 0085 if( opt ) 0086 formatString = QLatin1Char( '{' ) + formatString + QLatin1Char( '}' ); 0087 0088 QStringx fmtx( formatString ); 0089 return fmtx.namedOptArgs( args ); 0090 } 0091 0092 QString 0093 Amarok::QStringx::namedOptArgs( const QMap<QString, QString> &args ) const 0094 { 0095 int pos = 0; 0096 return parse( &pos, args ); 0097 } 0098 0099 Amarok::QStringx::CharType 0100 Amarok::QStringx::testChar( int *pos ) const 0101 { 0102 if( *pos >= length() ) 0103 return CTNone; 0104 0105 QChar c = this->at( *pos ); 0106 0107 if( c == QLatin1Char( '\\' ) ) 0108 { 0109 ( *pos )++; 0110 return ( *pos >= length() ) ? CTNone : CTRegular; 0111 } 0112 0113 if( c == QLatin1Char( '{' ) ) 0114 return CTBraceOpen; 0115 0116 if( c == QLatin1Char( '}' ) ) 0117 return CTBraceClose; 0118 0119 if( c == QLatin1Char( '[' ) ) 0120 return CTBracketOpen; 0121 0122 if( c == QLatin1Char( QLatin1Char(':') ) ) 0123 return CTBracketSeparator; 0124 0125 if( c == QLatin1Char( ']' ) ) 0126 return CTBracketClose; 0127 0128 if( c == QLatin1Char( '%' ) ) 0129 return CTToken; 0130 0131 return CTRegular; 0132 } 0133 0134 QString 0135 Amarok::QStringx::parseToken( int *pos, const QMap<QString, QString> &dict ) const 0136 { 0137 if( testChar( pos ) != CTToken ) 0138 return QString(); 0139 0140 ( *pos )++; 0141 0142 CharType ct = testChar( pos ); 0143 QString key; 0144 0145 while( ct == CTRegular ) 0146 { 0147 key += this->at( *pos ); 0148 ( *pos )++; 0149 ct = testChar( pos ); 0150 } 0151 0152 if( ct == CTToken ) 0153 { 0154 ( *pos )++; 0155 return dict.value( key ); 0156 } 0157 0158 *pos -= key.length(); 0159 0160 return QLatin1String( "%" ); 0161 } 0162 0163 QString 0164 Amarok::QStringx::parseBraces( int *pos, const QMap<QString, QString> &dict ) const 0165 { 0166 if( testChar( pos ) != CTBraceOpen ) 0167 return QString(); 0168 0169 ( *pos )++; 0170 0171 int retPos = *pos; 0172 QString result; 0173 bool isPritable = true; 0174 CharType ct = testChar( pos ); 0175 0176 while( ct != CTNone && ct != CTBraceClose ) 0177 { 0178 switch( ct ) 0179 { 0180 case CTBraceOpen: 0181 { 0182 result += parseBraces( pos, dict ); 0183 break; 0184 } 0185 case CTBracketOpen: 0186 { 0187 result += parseBrackets( pos, dict ); 0188 break; 0189 } 0190 case CTToken: 0191 { 0192 QString part = parseToken( pos, dict ); 0193 if( part.isEmpty() ) 0194 isPritable = false; 0195 0196 result += part; 0197 break; 0198 } 0199 0200 default: 0201 { 0202 result += this->at( *pos ); 0203 ( *pos )++; 0204 } 0205 } 0206 0207 ct = testChar( pos ); 0208 } 0209 0210 if( ct == CTBraceClose ) 0211 { 0212 ( *pos )++; 0213 if( isPritable ) 0214 return result; 0215 0216 return QString(); 0217 } 0218 0219 *pos = retPos; 0220 return QLatin1String( "{" ); 0221 } 0222 0223 QString 0224 Amarok::QStringx::parseBrackets( int *pos, const QMap<QString, QString> &dict ) const 0225 { 0226 if( testChar( pos ) != CTBracketOpen ) 0227 return QString(); 0228 0229 ( *pos )++; 0230 0231 // Check if next char is % 0232 if( testChar( pos ) != CTToken ) 0233 return QLatin1String( "[" ); 0234 0235 int retPos = *pos; 0236 0237 ( *pos )++; 0238 0239 // Parse token manually (not by calling parseToken), because we need token name. 0240 CharType ct = testChar( pos ); 0241 QString key; 0242 0243 while( ct == CTRegular ) 0244 { 0245 key += this->at( *pos ); 0246 ( *pos )++; 0247 ct = testChar( pos ); 0248 } 0249 0250 if( ct != CTToken || key.isEmpty() ) 0251 { 0252 *pos = retPos; 0253 return QLatin1String( "[" ); 0254 } 0255 0256 ( *pos )++; 0257 0258 QString replacement; 0259 0260 // Parse replacement string if we have one 0261 if( testChar( pos ) == CTBracketSeparator ) 0262 { 0263 ( *pos )++; 0264 0265 ct = testChar( pos ); 0266 0267 while( ct != CTNone && ct != CTBracketClose ) 0268 { 0269 switch( ct ) 0270 { 0271 case CTBraceOpen: 0272 { 0273 replacement += parseBraces( pos, dict ); 0274 break; 0275 } 0276 case CTBracketOpen: 0277 { 0278 replacement += parseBrackets( pos, dict ); 0279 break; 0280 } 0281 case CTToken: 0282 { 0283 replacement += parseToken( pos, dict ); 0284 break; 0285 } 0286 0287 default: 0288 { 0289 replacement += this->at( *pos ); 0290 ( *pos )++; 0291 } 0292 } 0293 0294 ct = testChar( pos ); 0295 } 0296 0297 if( ct == CTNone ) 0298 { 0299 *pos = retPos; 0300 return QLatin1String( "[" ); 0301 } 0302 } 0303 0304 if( testChar( pos ) == CTBracketClose ) 0305 { 0306 ( *pos )++; 0307 0308 if( !dict.value( key ).isEmpty() ) 0309 return dict.value( key ); 0310 0311 if( !replacement.isEmpty() ) 0312 return replacement; 0313 0314 if( !dict.value( QLatin1String( "default_" ) + key ).isEmpty() ) 0315 return dict.value( QLatin1String( "default_" ) + key ); 0316 0317 return QLatin1String( "Unknown " ) + key; 0318 } 0319 0320 *pos = retPos; 0321 return QLatin1String( "[" ); 0322 } 0323 0324 QString 0325 Amarok::QStringx::parse( int *pos, const QMap<QString, QString> &dict ) const 0326 { 0327 CharType ct = testChar( pos ); 0328 QString result; 0329 0330 while( ct != CTNone ) 0331 { 0332 switch( ct ) 0333 { 0334 case CTBraceOpen: 0335 { 0336 result += parseBraces( pos, dict ); 0337 break; 0338 } 0339 case CTBracketOpen: 0340 { 0341 result += parseBrackets( pos, dict ); 0342 break; 0343 } 0344 case CTToken: 0345 { 0346 result += parseToken( pos, dict ); 0347 break; 0348 } 0349 0350 default: 0351 { 0352 result += this->at( *pos ); 0353 ( *pos )++; 0354 } 0355 } 0356 0357 ct = testChar( pos ); 0358 } 0359 0360 return result; 0361 }