| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2015 The Qt Company Ltd. |
| 4 | ** Contact: http://www.qt.io/licensing/ |
| 5 | ** |
| 6 | ** This file is part of the QtVersit module of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:LGPL21$ |
| 9 | ** Commercial License Usage |
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 11 | ** accordance with the commercial license agreement provided with the |
| 12 | ** Software or, alternatively, in accordance with the terms contained in |
| 13 | ** a written agreement between you and The Qt Company. For licensing terms |
| 14 | ** and conditions see http://www.qt.io/terms-conditions. For further |
| 15 | ** information use the contact form at http://www.qt.io/contact-us. |
| 16 | ** |
| 17 | ** GNU Lesser General Public License Usage |
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
| 19 | ** General Public License version 2.1 or version 3 as published by the Free |
| 20 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and |
| 21 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the |
| 22 | ** following information to ensure the GNU Lesser General Public License |
| 23 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and |
| 24 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| 25 | ** |
| 26 | ** As a special exception, The Qt Company gives you certain additional |
| 27 | ** rights. These rights are described in The Qt Company LGPL Exception |
| 28 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| 29 | ** |
| 30 | ** $QT_END_LICENSE$ |
| 31 | ** |
| 32 | ****************************************************************************/ |
| 33 | |
| 34 | #include "qversitwriter.h" |
| 35 | #include "qversitwriter_p.h" |
| 36 | |
| 37 | #include <QtCore/qbuffer.h> |
| 38 | #include <QtCore/qtextcodec.h> |
| 39 | |
| 40 | #include "qversitutils_p.h" |
| 41 | |
| 42 | QT_BEGIN_NAMESPACE_VERSIT |
| 43 | |
| 44 | /*! |
| 45 | \class QVersitWriter |
| 46 | \brief The QVersitWriter class writes Versit documents such as vCards to a device. |
| 47 | \ingroup versit |
| 48 | \inmodule QtVersit |
| 49 | |
| 50 | QVersitWriter converts a QVersitDocument into its textual representation. |
| 51 | QVersitWriter supports writing to an abstract I/O device |
| 52 | which can be for example a file or a memory buffer. |
| 53 | The writing can be done asynchronously and the waitForFinished() |
| 54 | function can be used to implement a blocking write. |
| 55 | |
| 56 | The serialization of the document is done in accordance with the type of the QVersitDocument |
| 57 | being written. The value of each QVersitProperty is encoded according to the type of object: |
| 58 | \list |
| 59 | \li \l{QString}{QStrings} are serialized verbatim, unless the default codec of the writer cannot |
| 60 | encode the string: in this case, UTF-8 is used to encode it (and the CHARSET parameter added to |
| 61 | the property, as per the vCard 2.1 specification). If the document type is vCard 2.1, |
| 62 | quoted-printable encoding may also be performed. |
| 63 | \li \l{QByteArray}{QByteArrays} are assumed to be binary data and are serialized as base-64 encoded |
| 64 | values. |
| 65 | \li \l{QVersitDocument}{QVersitDocuments} are serialized as a nested document (eg. as per the |
| 66 | AGENT property in vCard). |
| 67 | \endlist |
| 68 | |
| 69 | \sa QVersitDocument, QVersitProperty |
| 70 | */ |
| 71 | |
| 72 | |
| 73 | /*! |
| 74 | * \enum QVersitWriter::Error |
| 75 | * This enum specifies an error that occurred during the most recent operation: |
| 76 | * \value NoError The most recent operation was successful |
| 77 | * \value UnspecifiedError The most recent operation failed for an undocumented reason |
| 78 | * \value IOError The most recent operation failed because of a problem with the device |
| 79 | * \value OutOfMemoryError The most recent operation failed due to running out of memory |
| 80 | * \value NotReadyError The most recent operation failed because there is an operation in progress |
| 81 | */ |
| 82 | |
| 83 | /*! |
| 84 | * \enum QVersitWriter::State |
| 85 | * Enumerates the various states that a reader may be in at any given time |
| 86 | * \value InactiveState Write operation not yet started |
| 87 | * \value ActiveState Write operation started, not yet finished |
| 88 | * \value CanceledState Write operation is finished due to cancelation |
| 89 | * \value FinishedState Write operation successfully completed |
| 90 | */ |
| 91 | |
| 92 | /*! |
| 93 | * \fn QVersitWriter::stateChanged(QVersitWriter::State state) |
| 94 | * The signal is emitted by the writer when its state has changed (eg. when it has finished |
| 95 | * writing to the device). |
| 96 | * \a state is the new state of the writer. |
| 97 | */ |
| 98 | |
| 99 | /*! Constructs a new writer. */ |
| 100 | QVersitWriter::QVersitWriter() : d(new QVersitWriterPrivate) |
| 101 | { |
| 102 | d->init(writer: this); |
| 103 | } |
| 104 | |
| 105 | /*! Constructs a new writer that writes to \a outputDevice. */ |
| 106 | QVersitWriter::QVersitWriter(QIODevice *outputDevice) : d(new QVersitWriterPrivate) |
| 107 | { |
| 108 | d->init(writer: this); |
| 109 | d->mIoDevice = outputDevice; |
| 110 | } |
| 111 | |
| 112 | /*! Constructs a new writer that appends to \a outputBytes. */ |
| 113 | QVersitWriter::QVersitWriter(QByteArray *outputBytes) : d(new QVersitWriterPrivate) |
| 114 | { |
| 115 | d->init(writer: this); |
| 116 | d->mOutputBytes.reset(other: new QBuffer); |
| 117 | d->mOutputBytes->setBuffer(outputBytes); |
| 118 | d->mOutputBytes->open(openMode: QIODevice::WriteOnly); |
| 119 | d->mIoDevice = d->mOutputBytes.data(); |
| 120 | } |
| 121 | |
| 122 | /*! |
| 123 | * Frees the memory used by the writer. |
| 124 | * Waits until a pending asynchronous writing has been completed. |
| 125 | */ |
| 126 | QVersitWriter::~QVersitWriter() |
| 127 | { |
| 128 | d->wait(); |
| 129 | delete d; |
| 130 | } |
| 131 | |
| 132 | /*! |
| 133 | * Sets the device used for writing to \a device. |
| 134 | * Does not take ownership of the device. |
| 135 | */ |
| 136 | void QVersitWriter::setDevice(QIODevice* device) |
| 137 | { |
| 138 | d->mOutputBytes.reset(other: 0); |
| 139 | d->mIoDevice = device; |
| 140 | } |
| 141 | |
| 142 | /*! |
| 143 | * Returns the device used for writing, or 0 if no device has been set. |
| 144 | */ |
| 145 | QIODevice* QVersitWriter::device() const |
| 146 | { |
| 147 | if (d->mOutputBytes.isNull()) |
| 148 | return d->mIoDevice; |
| 149 | else |
| 150 | return 0; |
| 151 | } |
| 152 | |
| 153 | /*! |
| 154 | * Sets the default codec for the writer to use for writing the entire output. |
| 155 | * |
| 156 | * If \a codec is NULL, the writer uses the codec according to the specification prescribed default. |
| 157 | * (for vCard 2.1, ASCII; for vCard 3.0, UTF-8). |
| 158 | */ |
| 159 | void QVersitWriter::setDefaultCodec(QTextCodec *codec) |
| 160 | { |
| 161 | d->mDefaultCodec = codec; |
| 162 | } |
| 163 | |
| 164 | /*! |
| 165 | * Returns the document's codec. |
| 166 | */ |
| 167 | QTextCodec* QVersitWriter::defaultCodec() const |
| 168 | { |
| 169 | return d->mDefaultCodec; |
| 170 | } |
| 171 | |
| 172 | /*! |
| 173 | * Returns the state of the writer. |
| 174 | */ |
| 175 | QVersitWriter::State QVersitWriter::state() const |
| 176 | { |
| 177 | return d->state(); |
| 178 | } |
| 179 | |
| 180 | /*! |
| 181 | * Returns the error encountered by the last operation. |
| 182 | */ |
| 183 | QVersitWriter::Error QVersitWriter::error() const |
| 184 | { |
| 185 | return d->error(); |
| 186 | } |
| 187 | |
| 188 | /*! |
| 189 | * Starts writing \a input to device() asynchronously. The serialization format is determined based |
| 190 | * on the contents of the input documents. |
| 191 | * |
| 192 | * Returns false if the output device has not been set or opened or if there is another asynchronous |
| 193 | * write operation already pending. Signal \l stateChanged() is emitted with parameter |
| 194 | * FinishedState when the writing has finished. |
| 195 | * |
| 196 | * The device must be already open. The client is responsible for closing it when finished. |
| 197 | */ |
| 198 | bool QVersitWriter::startWriting(const QList<QVersitDocument>& input) |
| 199 | { |
| 200 | return startWriting(input, type: QVersitDocument::InvalidType); |
| 201 | } |
| 202 | |
| 203 | /*! |
| 204 | * Starts writing \a input to device() asynchronously using the serialization format specified by \a |
| 205 | * type. If \a type is QVersitDocument::InvalidType, the format will be determined based on the |
| 206 | * contents of the input documents. |
| 207 | * |
| 208 | * Returns false if the output device has not been set or opened or if there is another asynchronous |
| 209 | * write operation already pending. Signal \l stateChanged() is emitted with parameter |
| 210 | * FinishedState when the writing has finished. |
| 211 | * |
| 212 | * The device must be already open. The client is responsible for closing it when finished. |
| 213 | */ |
| 214 | bool QVersitWriter::startWriting(const QList<QVersitDocument>& input, QVersitDocument::VersitType type) |
| 215 | { |
| 216 | d->mInput = input; |
| 217 | if (d->state() == ActiveState || d->isRunning()) { |
| 218 | d->setError(QVersitWriter::NotReadyError); |
| 219 | return false; |
| 220 | } else if (!d->mIoDevice || !d->mIoDevice->isWritable()) { |
| 221 | d->setError(QVersitWriter::IOError); |
| 222 | return false; |
| 223 | } else { |
| 224 | d->setState(ActiveState); |
| 225 | d->setError(NoError); |
| 226 | d->setDocumentType(type); |
| 227 | d->start(); |
| 228 | return true; |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | /*! |
| 233 | * Starts writing \a input to device() asynchronously. The serialization format is determined based |
| 234 | * on the contents of the input documents. |
| 235 | * |
| 236 | * Returns false if the output device has not been set or opened or if there is another asynchronous |
| 237 | * write operation already pending. Signal \l stateChanged() is emitted with parameter |
| 238 | * FinishedState when the writing has finished. |
| 239 | * |
| 240 | * The device must be already open. The client is responsible for closing it when finished. |
| 241 | */ |
| 242 | bool QVersitWriter::startWriting(const QVersitDocument& input) |
| 243 | { |
| 244 | return startWriting(input: QList<QVersitDocument>() << input, type: QVersitDocument::InvalidType); |
| 245 | } |
| 246 | |
| 247 | /*! |
| 248 | * Starts writing \a input to device() asynchronously using the serialization format specified by \a |
| 249 | * type. If \a type is QVersitDocument::InvalidType, the format will be determined based on the |
| 250 | * contents of the input documents. |
| 251 | * |
| 252 | * Returns false if the output device has not been set or opened or if there is another asynchronous |
| 253 | * write operation already pending. Signal \l stateChanged() is emitted with parameter |
| 254 | * FinishedState when the writing has finished. |
| 255 | * |
| 256 | * The device must be already open. The client is responsible for closing it when finished. |
| 257 | */ |
| 258 | bool QVersitWriter::startWriting(const QVersitDocument& input, QVersitDocument::VersitType type) |
| 259 | { |
| 260 | return startWriting(input: QList<QVersitDocument>() << input, type); |
| 261 | } |
| 262 | |
| 263 | /*! |
| 264 | * Attempts to asynchronously cancel the write request. |
| 265 | */ |
| 266 | void QVersitWriter::cancel() |
| 267 | { |
| 268 | d->setCanceling(true); |
| 269 | } |
| 270 | |
| 271 | /*! |
| 272 | * If the state is ActiveState, blocks until the writer has finished writing or \a msec milliseconds |
| 273 | * has elapsed, returning true if it successfully finishes or is cancelled by the user. |
| 274 | * If \a msec is negative or zero, the function blocks until the writer has finished, regardless of |
| 275 | * how long it takes. |
| 276 | * If the state is FinishedState, returns true immediately. |
| 277 | * Otherwise, returns false immediately. |
| 278 | */ |
| 279 | bool QVersitWriter::waitForFinished(int msec) |
| 280 | { |
| 281 | State state = d->state(); |
| 282 | if (state != InactiveState) { |
| 283 | if (msec <= 0) |
| 284 | return d->wait(ULONG_MAX); |
| 285 | else |
| 286 | return d->wait(time: msec); |
| 287 | } else { |
| 288 | return false; |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | #include "moc_qversitwriter.cpp" |
| 293 | |
| 294 | QT_END_NAMESPACE_VERSIT |
| 295 | |