/* -*- c++ -*- * * giftmessage.cpp * * Copyright (C) 2003 Sebastian Sauer * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include "giftmessage.h" //#include "giftmessage.moc" /********************************************************** * GiftMessageItem */ GiftMessageItem::GiftMessageItem(GiftMessageItem* ParentItem) { this->ParentItem = ParentItem; } GiftMessageItem::~GiftMessageItem() { clearMessage(); // to free the SubCommands } void GiftMessageItem::clearMessage() { MainKey = QString::null; MainValue = QString::null; Arguments.clear(); for(QValueList::iterator it = SubCommands.begin(); it != SubCommands.end(); ++it) delete (*it); } QString GiftMessageItem::getMessage() { // add Mainkey(Mainvalue) QString result = escapeString(MainKey, true); if(! MainValue.isEmpty()) result += QString("(") + escapeString(MainValue) + QString(")"); if(Arguments.count() > 0 || SubCommands.count() > 0) { if(ParentItem) result += QString("{"); // Subcommand's always have {...} around the Arguments and sub/child-Subcommand's // add the Argumentkey(Argumentvalue) pairs for(QMap::iterator mit = Arguments.begin(); mit != Arguments.end(); ++mit) { result += QString(" ") + escapeString(*mit, true); if(! mit.data().isEmpty()) result += QString("(") + escapeString(mit.data()) + QString(")"); } // add the Subcommand's for(QValueList::iterator vit = SubCommands.begin(); vit != SubCommands.end(); ++vit) result += QString(" ") + (*vit)->getMessage(); if(ParentItem) result += QString("}"); } if(! ParentItem) result += QString(";"); kdDebug() << "GiftMessageItem::getMessage() message='" << result << "'" << endl; return result; } GiftMessageItem::enumSetMessage GiftMessageItem::setMessage(const QString& message) { // remove already parsed stuff from previous calls clearMessage(); // Check if the string contains the endmessage-char ; // Those char is needed to start the parsing. As long as it's missing we just // remember the parameters to parse the message later. That way we could send // the message in parts and only if the finalizer is there the parsing starts. if(! msg.isEmpty()) msg += QString(" "); int endpos = getToken(message, ";", 0); if(endpos < 0) { msg += message; return smFinalizerMissing; } msg += message.left(endpos); // ignore everything after the ;-char // simplify the message to have an easier parsable string msg = simplifyString(msg); kdDebug() << "GiftMessageItem::setMessage() message='" << msg << "'" << endl; // Let's start parsing the Subcommand's first QString subkey, subvalue, submsg; int subpos = 0; bool subok; do { // walk through the msg and try to get a subcommand subok = getNextSubcommand(msg, subpos, subkey, subvalue, submsg); if(! subok) break; kdDebug() << "GiftMessageItem::setMessage() subkey='" << subkey << "' subvalue='" << subvalue << "' submsg='" << submsg << "'" << endl; if(subkey.isEmpty()) continue; // add a new GiftMessageItem sub-/childitem GiftMessageItem* item = new GiftMessageItem(this); QString s = escapeString(subkey, true); if(! subvalue.isEmpty()) s += QString("(") + escapeString(subvalue) + QString(")"); enumSetMessage smresult = item->setMessage(s + QString(" ") + submsg + QString(";")); // recursive, don't escapeString() submsg! if(smresult == smOk) SubCommands.append(item); else { kdDebug() << QString("GiftMessageItem::setMessage() cause of a parse-error free the subcommand '%1'").arg(subkey) << endl; delete item; return smresult; } } while(1); // What stays are the Argumentkey(optional Argumentvalue) pairs. So let's // parse them now. int argpos = 0; int argcount = 0; bool argok; do { QString argkey, argvalue; argok = getNextArg(msg, argpos, argkey, argvalue); if(! argok) break; if(argkey.isEmpty()) argpos++; else { if(argcount == 0) { // first argument is always the Commandkey(Commandvalue) kdDebug() << "GiftMessageItem::setMessage() Commandkey='" << argkey << "' Commandvalue='" << argvalue << "'" << endl; MainKey = argkey; MainValue = argvalue; } else { // else just note the argument as Argumentkey(Argumentvalue) kdDebug() << "GiftMessageItem::setMessage() Argumentkey='" << argkey << "' Argumentvalue='" << argvalue << "'" << endl; Arguments.replace(argkey, argvalue); } argcount++; } } while(1); msg = QString::null; // clear the msg cause the job is done return smOk; } /********************************************************** * Parser-functions for GiftMessageItem's */ bool GiftMessageItem::getPrevArg(const QString& msg, int& pos, QString& key, QString& value) { int prevspacepos = getToken(msg, " ", pos - 1, true); int prevendpos = getToken(msg, ")", pos - 1, true); if(prevendpos >= 0 && (prevspacepos < 0 || prevspacepos < prevendpos)) { // Argumentkey(Argumentvalue) int prevstartpos = getToken(msg, "(", prevendpos - 1, true); if(prevstartpos >= 0) { int spacepos = getToken(msg, " ", prevstartpos - 1, true); if(spacepos >= 0) { key = msg.mid(spacepos, prevstartpos - spacepos); pos = spacepos; kdDebug() << "GiftMessageItem::getPrevArg() (spacepos >= 0) key=" << key << endl; } else { key = msg.left(prevstartpos); pos = 0; kdDebug() << "GiftMessageItem::getPrevArg() (spacepos < 0) key=" << key << endl; } value = msg.mid(prevstartpos + 1, prevendpos - prevstartpos - 1); kdDebug() << "GiftMessageItem::getPrevArg() key='" << key << "' value='" << value << "'" << endl; return true; } else { kdDebug() << "GiftMessageItem::getPrevArg() parse-error !!!" << endl; return false; } } else if(pos > 0) { // Argumentkey without the optional (Argumentvalue) if(prevspacepos >= 0) { key = msg.mid(prevspacepos, pos - prevspacepos); pos = prevspacepos; } else { key = msg.left(pos); pos = 0; } kdDebug() << "GiftMessageItem::getPrevArg() key=" << key << endl; return true; } return false; } bool GiftMessageItem::getNextArg(const QString& msg, int& pos, QString& key, QString& value) { int spacepos = getToken(msg, " ", pos); int startpos = getToken(msg, "(", pos); if(startpos >= 0 && (spacepos < 0 || spacepos > startpos)) { // Argumentkey(Argumentvalue) int endpos = getToken(msg, ")", startpos + 1); key = msg.mid(pos, startpos - pos); if(endpos > startpos) { value = msg.mid(startpos + 1, endpos - startpos - 1); pos = endpos + 1; return true; } else { kdDebug() << "GiftMessageItem::getNextArg() parse-error !!!" << endl; return false; } } else if(pos < int(msg.length())) { // Argumentkey without optional (Argumentvalue) if(spacepos >= 0) { key = msg.mid(pos, spacepos - pos); pos = spacepos; } else { key = msg.right(msg.length() - pos); pos = 0; } return true; } return false; } bool GiftMessageItem::getNextSubcommand(QString& msg, int& pos, QString& subkey, QString& subvalue, QString& submsg) { int startpos = getToken(msg, "{", pos + 1); if(startpos < 0) return false; int endpos = getToken(msg, "}", startpos + 1); if(endpos < startpos) return false; submsg = msg.mid(startpos + 1, endpos - startpos - 1); msg = simplifyString( msg.left(startpos) + QString(" ") + msg.right(msg.length() - endpos - 1) ); kdDebug() << "GiftMessageItem::getNextSubcommand() 1 submsg='" << submsg << "' msg='" << msg << "'" << endl; int argpos1 = startpos; QString argkey1, argvalue1; if( getPrevArg(msg, argpos1, argkey1, argvalue1) ) { subkey = escapeString(argkey1, true); if(! argvalue1.isEmpty()) { // We found the subcommandkey(subcommandvalue){...} subvalue = escapeString(argvalue1); msg = simplifyString(msg.remove(argpos1, startpos - argpos1)); } else { // let's try to seek for the subcommandkey(subcommandvalue) again cause // per definition something like subcommandkey{...}(subcommandvalue) is // allowed too. int argpos2 = argpos1; while(msg.at(argpos2) == QChar(' ')) argpos2++; // ignore leading whitespaces... QString argkey2, argvalue2; //kdDebug() << "GiftMessageItem::getNextSubcommand() subkey='" << subkey << "'" << endl; if( getNextArg(msg, argpos2, argkey2, argvalue2) ) { if(subkey == escapeString(argkey2, true) && ! argvalue2.isEmpty()) { // We found a subcommandkey{...}(subcommandvalue) case subvalue = escapeString(argvalue2); startpos = argpos2; kdDebug() << "GiftMessageItem::getNextSubcommand() subkey='" << subkey << "' subvalue='" << subvalue << "'" << endl; } else kdDebug() << "GiftMessageItem::getNextSubcommand() (subkey != argkey2) subkey='" << subkey << "' argkey2='" << argkey2 << "'" << endl; } else kdDebug() << "GiftMessageItem::getNextSubcommand() Couldn't find getNextArg() startpos=" << startpos << " argpos1=" << argpos1 << endl; if(argpos1 < startpos) msg = simplifyString(msg.remove(argpos1, startpos - argpos1)); } pos = argpos1; return true; } else { pos = startpos; kdDebug() << "GiftMessageItem::getNextSubcommand() 4 NO SUBCOMMANDKEY !!!" << endl; return true; } kdDebug() << "GiftMessageItem::getNextSubcommand() 5 FAILED !!!" << endl; return false; } int GiftMessageItem::getToken(const QString& message, const QString& token, const int startpos, bool backwards) { int pos = startpos; do { pos = backwards ? message.findRev(token, pos) : message.find(token, pos); if(pos < 0) break; if(! isEscaped(message, pos)) return pos; pos = backwards ? pos - 1 : pos + 1; } while(1); return -1; } QString GiftMessageItem::simplifyString(const QString& s) { return s.stripWhiteSpace().simplifyWhiteSpace().replace(QRegExp("\\s(\\(|\\)|\\{|\\}|\\;)"), "\\1"); } bool GiftMessageItem::isEscaped(const QString& s, const int pos) { int i = pos - 1; while(i >= 0 && s.at(i) == '\\') i--; i = pos - i + 1; return (i > 0 && i % 2 != 0); } QString GiftMessageItem::escapeString(const QString& msg, bool EscapeWhiteSpaces) { QRegExp rx; if(EscapeWhiteSpaces) rx.setPattern("(\\\\|\\s|\\(|\\)|\\{|\\}|\\;)"); else rx.setPattern("(\\\\|\\(|\\)|\\{|\\}|\\;)"); QString s = msg.stripWhiteSpace(); int pos = -1; do { pos = s.find(rx, pos + 1); if(pos < 0) break; if(! isEscaped(s, pos)) { s.insert(pos, "\\"); pos++; } } while(1); return s; } QString GiftMessageItem::unescapeString(const QString& msg) { QRegExp rx("(\\\\|\\s|\\(|\\)|\\{|\\}|\\;)"); QString s = msg.stripWhiteSpace(); int pos = -1; do { pos = s.find(rx, pos + 1); if(pos < 0) break; if(isEscaped(s, pos)) { s.remove(pos - 1, 1); pos--; } } while(1); return s; } /********************************************************** * GiftMessage */ GiftMessage::GiftMessage() { RootItem = 0; } GiftMessage::~GiftMessage() { delete RootItem; } QString GiftMessage::getMainKey() { return (RootItem && smresult == GiftMessageItem::smOk) ? RootItem->getMainKey() : QString::null; } QString GiftMessage::getMainValue() { return (RootItem && smresult == GiftMessageItem::smOk) ? RootItem->getMainValue() : QString::null; } QString GiftMessage::getArgumentValue(const QString& key) { return (RootItem && smresult == GiftMessageItem::smOk) ? RootItem->getArgumentValue(key) : QString::null; } QString GiftMessage::getMessage() { return (RootItem && smresult == GiftMessageItem::smOk) ? RootItem->getMessage() : QString::null; } GiftMessageItem::enumSetMessage GiftMessage::setMessage(const QString& message) { if(smresult != GiftMessageItem::smFinalizerMissing) { // remove an old already handled RootItem delete RootItem; RootItem = 0; } if(! RootItem) RootItem = new GiftMessageItem(); smresult = RootItem->setMessage(message); return smresult; }