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 }