Header.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2005-2007 by Peter Dimov.
00004 
00005 This file is part of Calitko (http://www.calitko.org).
00006 
00007 Calitko is free software; you can redistribute it and/or modify
00008 it under the terms of the GNU General Public License as published by
00009 the Free Software Foundation; either version 2 of the License, or
00010 (at your option) any later version.
00011 
00012 Calitko is distributed in the hope that it will be useful,
00013 but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Calitko; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 
00021 */
00022 
00023 #include "Qt.h"
00024 #include "Header.h"
00025 #include <algorithm>
00026 #include <functional>
00027 
00028 using namespace Http;
00029 
00030 namespace Http {
00031 
00032 class HeaderPrivate
00033 {
00034 public:
00035     inline virtual              ~HeaderPrivate() {}
00036 
00037     struct FieldInfo
00038     {
00039         QString                 originalCaseFieldName;
00040         QStringList             fieldValues;
00041 
00042         FieldInfo() : originalCaseFieldName(), fieldValues() {}
00043     };
00044 
00045     QMap <QString, FieldInfo>   fields;
00046     QStringList                 orderedFieldList;
00047     bool                        valid;
00048 
00049     HeaderPrivate() :   fields(), orderedFieldList(), valid (false) {}
00050 };
00051 
00052 } // namespace Http;
00053 
00057 Header::Header()
00058     : d(new HeaderPrivate)
00059 {
00060     d->valid = true;
00061 }
00062 
00066 Header::Header (const Header &header)
00067     : d(new HeaderPrivate)
00068 {
00069     d->valid            = header.d->valid;
00070     d->fields           = header.d->fields;
00071     d->orderedFieldList = header.d->orderedFieldList;
00072 }
00073 
00082 Header::Header (const QString &str)
00083     : d(new HeaderPrivate)
00084 {
00085     d->valid = parse(str);
00086 }
00087 
00091 Header::~Header()
00092 {
00093     delete d;
00094 }
00095 
00099 Header & Header::operator= (const Header &header)
00100 {
00101     d->valid            = header.d->valid;
00102     d->fields           = header.d->fields;
00103     d->orderedFieldList = header.d->orderedFieldList;
00104     return *this;
00105 }
00106 
00111 bool Header::operator== (const Header &other) const
00112 {
00113     return toString() == other.toString();
00114 }
00115 
00120 bool Header::operator!= (const Header &other) const
00121 {
00122     return !(*this == other);
00123 }
00124 
00130 bool Header::isValid() const
00131 {
00132     return d->valid;
00133 }
00134 
00135 struct lineAppender : public std::unary_function<QString, void>
00136 {
00137     QStringList& m_lines;
00138     lineAppender( QStringList& lines ) : m_lines( lines ) {
00139     }
00140     void operator()(QString& line) {
00141         if (!line.isEmpty()) {
00142             if (line[0].isSpace()) {
00143                 if (!m_lines.isEmpty()) {
00144                     m_lines.last() += " ";
00145                     m_lines.last() += line.trimmed();
00146                 }
00147             } else {
00148                 m_lines.append(line);
00149             }
00150         }
00151     }
00152 };
00153 
00154 QStringList Header::splitStringIntoLines (const QString &str)
00155 {
00156     QStringList lst;
00157     int pos = str.indexOf('\n');
00158     if (pos > 0 && str.at(pos - 1) == '\r')
00159         lst = str.trimmed().split("\r\n");
00160     else
00161         lst = str.trimmed().split("\n");
00162     lst.removeAll(QString()); // No empties
00163     return lst;
00164 }
00165 
00166 bool Header::parseAllLines (const QStringList& lines)
00167 {
00168     int number = 0;
00169     QStringList::const_iterator it = lines.begin();
00170     for(; it != lines.end(); ++it) {
00171         if (!parseLine(*it, number++)) {
00172             return false;
00173         }
00174     }
00175     return true;
00176 }
00177 
00188 bool Header::parse (const QString &str)
00189 {
00190     d->fields.clear();
00191     d->orderedFieldList.clear();
00192     d->valid = true;
00193 
00194     QStringList lst = splitStringIntoLines (str);
00195     if (lst.isEmpty())
00196         return true;
00197 
00198     QStringList lines;
00199     std::for_each (lst.begin(), lst.end(), lineAppender(lines));
00200     d->valid = parseAllLines (lines);
00201     return d->valid;
00202 }
00203 
00206 void Header::setValid (bool v)
00207 {
00208     d->valid = v;
00209 }
00210 
00220 QString Header::fieldValue (const QString &fieldName) const
00221 {
00222     QString nameLower (fieldName.toLower());
00223     if (!d->fields.contains (nameLower))
00224         return QString();
00225     else
00226         return d->fields [nameLower].fieldValues.join (", ");
00227 }
00228 
00235 QStringList Header::fields() const
00236 {
00237     return d->fields.keys();
00238 }
00239 
00247 bool Header::hasField (const QString &fieldName) const
00248 {
00249     return d->fields.contains (fieldName.toLower());
00250 }
00251 
00264 void Header::setFieldValue (const QString &fieldName,
00265                             const QString &fieldValue)
00266 {
00267     QString nameLower (fieldName.toLower());
00268     // Make sure old values are discarded.
00269     d->fields.remove (nameLower);
00270     addFieldValues(fieldName, fieldValue.split(",", QString::SkipEmptyParts));
00271 }
00272 
00280 void Header::addFieldValue (const QString &fieldName,
00281                             const QString &fieldValue)
00282 {
00283     QString nameLower (fieldName.toLower());
00284     d->fields [nameLower].fieldValues.push_back (fieldValue.trimmed());
00285     // We may have just created a new field:
00286     d->fields [nameLower].originalCaseFieldName = fieldName;
00287     if (!d->orderedFieldList.contains (nameLower))
00288         d->orderedFieldList.append (nameLower);
00289 }
00290 
00298 void Header::addFieldValues (const QString &fieldName,
00299                              const QStringList &fieldValues)
00300 {
00301     foreach (QString value, fieldValues)
00302         addFieldValue (fieldName, value);
00303 }
00304 
00312 QStringList Header::fieldValues (const QString &fieldName) const
00313 {
00314     QString nameLower (fieldName.toLower());
00315     if (!d->fields.contains (nameLower))
00316         return QStringList();
00317     else
00318         return d->fields [nameLower].fieldValues;
00319 }
00320 
00326 void Header::removeField (const QString &fieldName)
00327 {
00328     QString nameLower (fieldName.toLower());
00329     if (d->fields.contains (nameLower)) {
00330         d->fields.remove (nameLower);
00331         d->orderedFieldList.removeAt (d->orderedFieldList.indexOf (nameLower));
00332     }
00333 }
00334 
00343 bool Header::parseLine (const QString &line, int)
00344 {
00345     int i = line.indexOf(':');
00346     if (i == -1)
00347         return false;
00348 
00349     addFieldValues (line.left (i).trimmed (),
00350                     line.mid (i + 1).split (",", QString::SkipEmptyParts));
00351     return true;
00352 }
00353 
00361 QString Header::toString() const
00362 {
00363     if (!isValid())
00364         return "";
00365 
00366     QString ret = "";
00367 
00368     foreach (QString fieldName, d->orderedFieldList) {
00369         int oldLength = ret.length();
00370         ret += d->fields [fieldName].originalCaseFieldName + ":";
00371         foreach (QString value, d->fields [fieldName].fieldValues) {
00372             // +1 for " ", +1 for ","
00373             if (ret.length() - oldLength + value.length() + 1 > 80) {
00374                 // +2 because of "\r\n"; the "\t" counts for the next line
00375                 oldLength = ret.length() + 2;
00376                 ret += "\r\n\t" + value.trimmed() + ",";
00377             }
00378             else
00379                 ret += " " + value.trimmed() + ",";
00380         }
00381         if (d->fields [fieldName].fieldValues.size() > 0)
00382             ret.chop(1); // Remove the last ",".
00383         ret += "\r\n"; // Terminate the line with CRLF.
00384     }
00385     return ret;
00386 }