From 9be00330af639043961f4d7b8ee1d3511e787931 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 19 Jan 2018 15:30:24 -0800 Subject: Examples: add an example that converts between different file formats This example converts to and from: - Binary JSON - CBOR - CBOR Diagnostic notation (output only) - JSON - Null (output only) - QDataStream - QVariant dump (output only) - Text - XML Change-Id: Ibab69e0efefb40bdbf94fffd150b59f8c0da3174 Reviewed-by: Allan Sandfeld Jensen --- .../serialization/convert/cborconverter.cpp | 406 ++++++++++++++++ .../corelib/serialization/convert/cborconverter.h | 85 ++++ examples/corelib/serialization/convert/convert.pro | 29 ++ examples/corelib/serialization/convert/converter.h | 103 +++++ .../serialization/convert/datastreamconverter.cpp | 273 +++++++++++ .../serialization/convert/datastreamconverter.h | 85 ++++ .../serialization/convert/jsonconverter.cpp | 212 +++++++++ .../corelib/serialization/convert/jsonconverter.h | 85 ++++ examples/corelib/serialization/convert/main.cpp | 234 ++++++++++ .../serialization/convert/nullconverter.cpp | 99 ++++ .../corelib/serialization/convert/nullconverter.h | 69 +++ .../serialization/convert/textconverter.cpp | 160 +++++++ .../corelib/serialization/convert/textconverter.h | 70 +++ .../corelib/serialization/convert/xmlconverter.cpp | 514 +++++++++++++++++++++ .../corelib/serialization/convert/xmlconverter.h | 69 +++ examples/corelib/serialization/serialization.pro | 1 + 16 files changed, 2494 insertions(+) create mode 100644 examples/corelib/serialization/convert/cborconverter.cpp create mode 100644 examples/corelib/serialization/convert/cborconverter.h create mode 100644 examples/corelib/serialization/convert/convert.pro create mode 100644 examples/corelib/serialization/convert/converter.h create mode 100644 examples/corelib/serialization/convert/datastreamconverter.cpp create mode 100644 examples/corelib/serialization/convert/datastreamconverter.h create mode 100644 examples/corelib/serialization/convert/jsonconverter.cpp create mode 100644 examples/corelib/serialization/convert/jsonconverter.h create mode 100644 examples/corelib/serialization/convert/main.cpp create mode 100644 examples/corelib/serialization/convert/nullconverter.cpp create mode 100644 examples/corelib/serialization/convert/nullconverter.h create mode 100644 examples/corelib/serialization/convert/textconverter.cpp create mode 100644 examples/corelib/serialization/convert/textconverter.h create mode 100644 examples/corelib/serialization/convert/xmlconverter.cpp create mode 100644 examples/corelib/serialization/convert/xmlconverter.h (limited to 'examples') diff --git a/examples/corelib/serialization/convert/cborconverter.cpp b/examples/corelib/serialization/convert/cborconverter.cpp new file mode 100644 index 0000000000..5c034680c2 --- /dev/null +++ b/examples/corelib/serialization/convert/cborconverter.cpp @@ -0,0 +1,406 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "cborconverter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static CborConverter cborConverter; +static CborDiagnosticDumper cborDiagnosticDumper; + +static const char optionHelp[] = + "convert-float-to-int=yes|no Write integers instead of floating point, if no\n" + " loss of precision occurs on conversion.\n" + "float16=yes|always|no Write using half-precision floating point.\n" + " If 'always', won't check for loss of precision.\n" + "float32=yes|always|no Write using single-precision floating point.\n" + " If 'always', won't check for loss of precision.\n" + "signature=yes|no Prepend the CBOR signature to the file output.\n" + ; + +static const char diagnosticHelp[] = + "extended=no|yes Use extended CBOR diagnostic format.\n" + "line-wrap=yes|no Split output into multiple lines.\n" + ; + +QT_BEGIN_NAMESPACE + +QDataStream &operator<<(QDataStream &ds, QCborSimpleType st) +{ + return ds << quint8(st); +} + +QDataStream &operator>>(QDataStream &ds, QCborSimpleType &st) +{ + quint8 v; + ds >> v; + st = QCborSimpleType(v); + return ds; +} + +QDebug &operator<<(QDebug &d, QCborSimpleType st) +{ + return d << quint8(st); +} + +QDataStream &operator<<(QDataStream &ds, QCborTag tag) +{ + return ds << quint64(tag); +} + +QDataStream &operator>>(QDataStream &ds, QCborTag &tag) +{ + quint64 v; + ds >> v; + tag = QCborTag(v); + return ds; +} + +QDebug &operator<<(QDebug &d, QCborTag tag) +{ + return d << quint64(tag); +} + +QT_END_NAMESPACE + +// We can't use QCborValue::toVariant directly because that would destroy +// non-string keys in CBOR maps (QVariantMap can't handle those). Instead, we +// have our own set of converter functions so we can keep the keys properly. + +static QVariant convertCborValue(const QCborValue &value); + +static QVariant convertCborMap(const QCborMap &map) +{ + VariantOrderedMap result; + result.reserve(map.size()); + for (auto pair : map) + result.append({ convertCborValue(pair.first), convertCborValue(pair.second) }); + return QVariant::fromValue(result); +} + +static QVariant convertCborArray(const QCborArray &array) +{ + QVariantList result; + result.reserve(array.size()); + for (auto value : array) + result.append(convertCborValue(value)); + return result; +} + +static QVariant convertCborValue(const QCborValue &value) +{ + if (value.isArray()) + return convertCborArray(value.toArray()); + if (value.isMap()) + return convertCborMap(value.toMap()); + return value.toVariant(); +} + +enum TrimFloatingPoint { Double, Float, Float16 }; +static QCborValue convertFromVariant(const QVariant &v, TrimFloatingPoint fpTrimming) +{ + if (v.userType() == QVariant::List) { + const QVariantList list = v.toList(); + QCborArray array; + for (const QVariant &v : list) + array.append(convertFromVariant(v, fpTrimming)); + + return array; + } + + if (v.userType() == qMetaTypeId()) { + const auto m = v.value(); + QCborMap map; + for (const auto &pair : m) + map.insert(convertFromVariant(pair.first, fpTrimming), + convertFromVariant(pair.second, fpTrimming)); + return map; + } + + if (v.userType() == QVariant::Double && fpTrimming != Double) { + float f = float(v.toDouble()); + if (fpTrimming == Float16) + return float(qfloat16(f)); + return f; + } + + return QCborValue::fromVariant(v); +} + +QString CborDiagnosticDumper::name() +{ + return QStringLiteral("cbor-dump"); +} + +Converter::Direction CborDiagnosticDumper::directions() +{ + return Out; +} + +Converter::Options CborDiagnosticDumper::outputOptions() +{ + return SupportsArbitraryMapKeys; +} + +const char *CborDiagnosticDumper::optionsHelp() +{ + return diagnosticHelp; +} + +bool CborDiagnosticDumper::probeFile(QIODevice *f) +{ + Q_UNUSED(f); + return false; +} + +QVariant CborDiagnosticDumper::loadFile(QIODevice *f, Converter *&outputConverter) +{ + Q_UNREACHABLE(); + Q_UNUSED(f); + Q_UNUSED(outputConverter); + return QVariant(); +} + +void CborDiagnosticDumper::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) +{ + QCborValue::DiagnosticNotationOptions opts = QCborValue::LineWrapped; + for (const QString &s : options) { + QStringList pair = s.split('='); + if (pair.size() == 2) { + if (pair.first() == "line-wrap") { + opts &= ~QCborValue::LineWrapped; + if (pair.last() == "yes") { + opts |= QCborValue::LineWrapped; + continue; + } else if (pair.last() == "no") { + continue; + } + } + if (pair.first() == "extended") { + opts &= ~QCborValue::ExtendedFormat; + if (pair.last() == "yes") + opts |= QCborValue::ExtendedFormat; + continue; + } + } + + fprintf(stderr, "Unknown CBOR diagnostic option '%s'. Available options are:\n%s", + qPrintable(s), diagnosticHelp); + exit(EXIT_FAILURE); + } + + QTextStream out(f); + out << convertFromVariant(contents, Double).toDiagnosticNotation(opts) + << endl; +} + +CborConverter::CborConverter() +{ + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaTypeStreamOperators(); + qRegisterMetaTypeStreamOperators(); + QMetaType::registerDebugStreamOperator(); + QMetaType::registerDebugStreamOperator(); +} + +QString CborConverter::name() +{ + return "cbor"; +} + +Converter::Direction CborConverter::directions() +{ + return InOut; +} + +Converter::Options CborConverter::outputOptions() +{ + return SupportsArbitraryMapKeys; +} + +const char *CborConverter::optionsHelp() +{ + return optionHelp; +} + +bool CborConverter::probeFile(QIODevice *f) +{ + if (QFile *file = qobject_cast(f)) { + if (file->fileName().endsWith(QLatin1String(".cbor"))) + return true; + } + return f->isReadable() && f->peek(3) == QByteArray("\xd9\xd9\xf7", 3); +} + +QVariant CborConverter::loadFile(QIODevice *f, Converter *&outputConverter) +{ + const char *ptr = nullptr; + if (auto file = qobject_cast(f)) + ptr = reinterpret_cast(file->map(0, file->size())); + + QByteArray mapped = QByteArray::fromRawData(ptr, ptr ? f->size() : 0); + QCborStreamReader reader(mapped); + if (!ptr) + reader.setDevice(f); + + if (reader.isTag() && reader.toTag() == QCborKnownTags::Signature) + reader.next(); + + QCborValue contents = QCborValue::fromCbor(reader); + qint64 offset = reader.currentOffset(); + if (reader.lastError()) { + fprintf(stderr, "Error loading CBOR contents (byte %lld): %s\n", offset, + qPrintable(reader.lastError().toString())); + fprintf(stderr, " bytes: %s\n", + (ptr ? mapped.mid(offset, 9) : f->read(9)).toHex(' ').constData()); + exit(EXIT_FAILURE); + } else if (offset < mapped.size() || (!ptr && f->bytesAvailable())) { + fprintf(stderr, "Warning: bytes remaining at the end of the CBOR stream\n"); + } + + if (outputConverter == nullptr) + outputConverter = &cborDiagnosticDumper; + else if (outputConverter == null) + return QVariant(); + else if (!outputConverter->outputOptions().testFlag(SupportsArbitraryMapKeys)) + return contents.toVariant(); + return convertCborValue(contents); +} + +void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) +{ + bool useSignature = true; + bool useIntegers = true; + enum { Yes, No, Always } useFloat16 = Yes, useFloat = Yes; + + for (const QString &s : options) { + QStringList pair = s.split('='); + if (pair.size() == 2) { + if (pair.first() == "convert-float-to-int") { + if (pair.last() == "yes") { + useIntegers = true; + continue; + } else if (pair.last() == "no") { + useIntegers = false; + continue; + } + } + + if (pair.first() == "float16") { + if (pair.last() == "no") { + useFloat16 = No; + continue; + } else if (pair.last() == "yes") { + useFloat16 = Yes; + continue; + } else if (pair.last() == "always") { + useFloat16 = Always; + continue; + } + } + + if (pair.first() == "float32") { + if (pair.last() == "no") { + useFloat = No; + continue; + } else if (pair.last() == "yes") { + useFloat = Yes; + continue; + } else if (pair.last() == "always") { + useFloat = Always; + continue; + } + } + + if (pair.first() == "signature") { + if (pair.last() == "yes") { + useSignature = true; + continue; + } else if (pair.last() == "no") { + useSignature = false; + continue; + } + } + } + + fprintf(stderr, "Unknown CBOR format option '%s'. Valid options are:\n%s", + qPrintable(s), optionHelp); + exit(EXIT_FAILURE); + } + + QCborValue v = convertFromVariant(contents, + useFloat16 == Always ? Float16 : useFloat == Always ? Float : Double); + QCborStreamWriter writer(f); + if (useSignature) + writer.append(QCborKnownTags::Signature); + + QCborValue::EncodingOptions opts; + if (useIntegers) + opts |= QCborValue::UseIntegers; + if (useFloat != No) + opts |= QCborValue::UseFloat; + if (useFloat16 != No) + opts |= QCborValue::UseFloat16; + v.toCbor(writer, opts); +} + diff --git a/examples/corelib/serialization/convert/cborconverter.h b/examples/corelib/serialization/convert/cborconverter.h new file mode 100644 index 0000000000..f0a89cb141 --- /dev/null +++ b/examples/corelib/serialization/convert/cborconverter.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CBORCONVERTER_H +#define CBORCONVERTER_H + +#include "converter.h" + +class CborDiagnosticDumper : public Converter +{ + // Converter interface +public: + QString name() override; + Direction directions() override; + Options outputOptions() override; + const char *optionsHelp() override; + bool probeFile(QIODevice *f) override; + QVariant loadFile(QIODevice *f, Converter *&outputConverter) override; + void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override; +}; + +class CborConverter : public Converter +{ +public: + CborConverter(); + + // Converter interface +public: + QString name() override; + Direction directions() override; + Options outputOptions() override; + const char *optionsHelp() override; + bool probeFile(QIODevice *f) override; + QVariant loadFile(QIODevice *f, Converter *&outputConverter) override; + void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override; +}; + +#endif // CBORCONVERTER_H diff --git a/examples/corelib/serialization/convert/convert.pro b/examples/corelib/serialization/convert/convert.pro new file mode 100644 index 0000000000..d9b1de41e3 --- /dev/null +++ b/examples/corelib/serialization/convert/convert.pro @@ -0,0 +1,29 @@ +QT += core +QT -= gui + +TARGET = convert +CONFIG += console +CONFIG -= app_bundle + +TEMPLATE = app + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/corelib/serialization/convert +INSTALLS += target + +SOURCES += main.cpp \ + cborconverter.cpp \ + jsonconverter.cpp \ + datastreamconverter.cpp \ + textconverter.cpp \ + xmlconverter.cpp \ + nullconverter.cpp + +HEADERS += \ + converter.h \ + cborconverter.h \ + jsonconverter.h \ + datastreamconverter.h \ + textconverter.h \ + xmlconverter.h \ + nullconverter.h diff --git a/examples/corelib/serialization/convert/converter.h b/examples/corelib/serialization/convert/converter.h new file mode 100644 index 0000000000..82e1fa1cb3 --- /dev/null +++ b/examples/corelib/serialization/convert/converter.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CONVERTER_H +#define CONVERTER_H + +#include +#include +#include +#include + +class VariantOrderedMap : public QVector> +{ +public: + VariantOrderedMap() = default; + VariantOrderedMap(const QVariantMap &map) + { + reserve(map.size()); + for (auto it = map.begin(); it != map.end(); ++it) + append({it.key(), it.value()}); + } +}; +using Map = VariantOrderedMap; +Q_DECLARE_METATYPE(Map) + +class Converter +{ +protected: + Converter(); + +public: + static Converter *null; + + enum Direction { + In = 1, Out = 2, InOut = 3 + }; + + enum Option { + SupportsArbitraryMapKeys = 0x01 + }; + Q_DECLARE_FLAGS(Options, Option) + + virtual ~Converter() = 0; + + virtual QString name() = 0; + virtual Direction directions() = 0; + virtual Options outputOptions() = 0; + virtual const char *optionsHelp() = 0; + virtual bool probeFile(QIODevice *f) = 0; + virtual QVariant loadFile(QIODevice *f, Converter *&outputConverter) = 0; + virtual void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) = 0; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Options) + +#endif // CONVERTER_H diff --git a/examples/corelib/serialization/convert/datastreamconverter.cpp b/examples/corelib/serialization/convert/datastreamconverter.cpp new file mode 100644 index 0000000000..7cdb844141 --- /dev/null +++ b/examples/corelib/serialization/convert/datastreamconverter.cpp @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "datastreamconverter.h" + +#include +#include +#include + +static const char optionHelp[] = + "byteorder=host|big|little Byte order to use.\n" + "version= QDataStream version (default: Qt 5.0).\n" + ; + +static const char signature[] = "qds"; + +static DataStreamDumper dataStreamDumper; +static DataStreamConverter DataStreamConverter; + +QDataStream &operator<<(QDataStream &ds, const VariantOrderedMap &map) +{ + ds << qint64(map.size()); + for (const auto &pair : map) + ds << pair.first << pair.second; + return ds; +} + +QDataStream &operator>>(QDataStream &ds, VariantOrderedMap &map) +{ + map.clear(); + + qint64 size; + ds >> size; + map.reserve(size); + + while (size-- > 0) { + VariantOrderedMap::value_type pair; + ds >> pair.first >> pair.second; + map.append(pair); + } + + return ds; +} + + +static QString dumpVariant(const QVariant &v, const QString &indent = QLatin1String("\n")) +{ + QString result; + QString indented = indent + QLatin1String(" "); + + int type = v.userType(); + if (type == qMetaTypeId() || type == QVariant::Map) { + const auto map = (type == QVariant::Map) ? + VariantOrderedMap(v.toMap()) : v.value(); + + result = QLatin1String("Map {"); + for (const auto &pair : map) { + result += indented + dumpVariant(pair.first, indented); + result.chop(1); // remove comma + result += QLatin1String(" => ") + dumpVariant(pair.second, indented); + + } + result.chop(1); // remove comma + result += indent + QLatin1String("},"); + } else if (type == QVariant::List) { + const QVariantList list = v.toList(); + + result = QLatin1String("List ["); + for (const auto &item : list) + result += indented + dumpVariant(item, indented); + result.chop(1); // remove comma + result += indent + QLatin1String("],"); + } else { + QDebug debug(&result); + debug.nospace() << v << ','; + } + return result; +} + +QString DataStreamDumper::name() +{ + return QStringLiteral("datastream-dump"); +} + +Converter::Direction DataStreamDumper::directions() +{ + return Out; +} + +Converter::Options DataStreamDumper::outputOptions() +{ + return SupportsArbitraryMapKeys; +} + +const char *DataStreamDumper::optionsHelp() +{ + return nullptr; +} + +bool DataStreamDumper::probeFile(QIODevice *f) +{ + Q_UNUSED(f); + return false; +} + +QVariant DataStreamDumper::loadFile(QIODevice *f, Converter *&outputConverter) +{ + Q_UNREACHABLE(); + Q_UNUSED(f); + Q_UNUSED(outputConverter); + return QVariant(); +} + +void DataStreamDumper::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) +{ + Q_UNUSED(options); + QString s = dumpVariant(contents); + s[s.size() - 1] = QLatin1Char('\n'); // replace the comma with newline + + QTextStream out(f); + out << s; +} + +DataStreamConverter::DataStreamConverter() +{ + qRegisterMetaType(); + qRegisterMetaTypeStreamOperators(); +} + +QString DataStreamConverter::name() +{ + return QStringLiteral("datastream"); +} + +Converter::Direction DataStreamConverter::directions() +{ + return InOut; +} + +Converter::Options DataStreamConverter::outputOptions() +{ + return SupportsArbitraryMapKeys; +} + +const char *DataStreamConverter::optionsHelp() +{ + return optionHelp; +} + +bool DataStreamConverter::probeFile(QIODevice *f) +{ + return f->isReadable() && f->peek(sizeof(signature) - 1) == signature; +} + +QVariant DataStreamConverter::loadFile(QIODevice *f, Converter *&outputConverter) +{ + if (!outputConverter) + outputConverter = &dataStreamDumper; + + char c; + if (f->read(sizeof(signature) -1) != signature || + !f->getChar(&c) || (c != 'l' && c != 'B')) { + fprintf(stderr, "Could not load QDataStream file: invalid signature.\n"); + exit(EXIT_FAILURE); + } + + QDataStream ds(f); + ds.setByteOrder(c == 'l' ? QDataStream::LittleEndian : QDataStream::BigEndian); + + std::underlying_type::type version; + ds >> version; + ds.setVersion(QDataStream::Version(version)); + + QVariant result; + ds >> result; + return result; +} + +void DataStreamConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) +{ + QDataStream::Version version = QDataStream::Qt_5_0; + auto order = QDataStream::ByteOrder(QSysInfo::ByteOrder); + for (const QString &option : options) { + const QStringList pair = option.split('='); + if (pair.size() == 2) { + if (pair.first() == "byteorder") { + if (pair.last() == "little") { + order = QDataStream::LittleEndian; + continue; + } else if (pair.last() == "big") { + order = QDataStream::BigEndian; + continue; + } else if (pair.last() == "host") { + order = QDataStream::ByteOrder(QSysInfo::ByteOrder); + continue; + } + } + if (pair.first() == "version") { + bool ok; + int n = pair.last().toInt(&ok); + if (ok) { + version = QDataStream::Version(n); + continue; + } + + fprintf(stderr, "Invalid version number '%s': must be a number from 1 to %d.\n", + qPrintable(pair.last()), QDataStream::Qt_DefaultCompiledVersion); + exit(EXIT_FAILURE); + } + } + + fprintf(stderr, "Unknown QDataStream formatting option '%s'. Available options are:\n%s", + qPrintable(option), optionHelp); + exit(EXIT_FAILURE); + } + + char c = order == QDataStream::LittleEndian ? 'l' : 'B'; + f->write(signature); + f->write(&c, 1); + + QDataStream ds(f); + ds.setVersion(version); + ds.setByteOrder(order); + ds << std::underlying_type::type(version); + ds << contents; +} diff --git a/examples/corelib/serialization/convert/datastreamconverter.h b/examples/corelib/serialization/convert/datastreamconverter.h new file mode 100644 index 0000000000..1b74abc54f --- /dev/null +++ b/examples/corelib/serialization/convert/datastreamconverter.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DATASTREAMCONVERTER_H +#define DATASTREAMCONVERTER_H + +#include "converter.h" + +class DataStreamDumper : public Converter +{ + // Converter interface +public: + QString name() override; + Direction directions() override; + Options outputOptions() override; + const char *optionsHelp() override; + bool probeFile(QIODevice *f) override; + QVariant loadFile(QIODevice *f, Converter *&outputConverter) override; + void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override; +}; + +class DataStreamConverter : public Converter +{ +public: + DataStreamConverter(); + + // Converter interface +public: + QString name() override; + Direction directions() override; + Options outputOptions() override; + const char *optionsHelp() override; + bool probeFile(QIODevice *f) override; + QVariant loadFile(QIODevice *f, Converter *&outputConverter) override; + void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override; +}; + +#endif // DATASTREAMCONVERTER_H diff --git a/examples/corelib/serialization/convert/jsonconverter.cpp b/examples/corelib/serialization/convert/jsonconverter.cpp new file mode 100644 index 0000000000..80d1cc6827 --- /dev/null +++ b/examples/corelib/serialization/convert/jsonconverter.cpp @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "jsonconverter.h" + +#include +#include +#include +#include +#include + +static JsonConverter jsonConverter; +static BinaryJsonConverter BinaryJsonConverter; + +static const char optionHelp[] = + "compact=no|yes Use compact JSON form.\n"; + +static QJsonDocument convertFromVariant(const QVariant &v) +{ + QJsonDocument doc = QJsonDocument::fromVariant(v); + if (!doc.isObject() && !doc.isArray()) { + fprintf(stderr, "Could not convert contents to JSON.\n"); + exit(EXIT_FAILURE); + } + return doc; +} + +JsonConverter::JsonConverter() +{ +} + +QString JsonConverter::name() +{ + return "json"; +} + +Converter::Direction JsonConverter::directions() +{ + return InOut; +} + +Converter::Options JsonConverter::outputOptions() +{ + return {}; +} + +const char *JsonConverter::optionsHelp() +{ + return optionHelp; +} + +bool JsonConverter::probeFile(QIODevice *f) +{ + if (QFile *file = qobject_cast(f)) { + if (file->fileName().endsWith(QLatin1String(".json"))) + return true; + } + + if (f->isReadable()) { + QByteArray ba = f->peek(1); + return ba == "{" || ba == "["; + } + return false; +} + +QVariant JsonConverter::loadFile(QIODevice *f, Converter *&outputConverter) +{ + if (!outputConverter) + outputConverter = this; + + QJsonParseError error; + QJsonDocument doc; + if (auto file = qobject_cast(f)) { + const char *ptr = reinterpret_cast(file->map(0, file->size())); + if (ptr) + doc = QJsonDocument::fromJson(QByteArray::fromRawData(ptr, file->size()), &error); + } + + if (doc.isNull()) + doc = QJsonDocument::fromJson(f->readAll(), &error); + if (error.error) { + fprintf(stderr, "Could not parse JSON content: offset %d: %s", + error.offset, qPrintable(error.errorString())); + exit(EXIT_FAILURE); + } + if (outputConverter == null) + return QVariant(); + return doc.toVariant(); +} + +void JsonConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) +{ + QJsonDocument::JsonFormat format = QJsonDocument::Indented; + for (const QString &s : options) { + if (s == QLatin1String("compact=no")) { + format = QJsonDocument::Indented; + } else if (s == QLatin1String("compact=yes")) { + format = QJsonDocument::Compact; + } else { + fprintf(stderr, "Unknown option '%s' to JSON output. Valid options are:\n%s", qPrintable(s), optionHelp); + exit(EXIT_FAILURE); + } + } + + f->write(convertFromVariant(contents).toJson(format)); +} + +QString BinaryJsonConverter::name() +{ + return "binary-json"; +} + +Converter::Direction BinaryJsonConverter::directions() +{ + return InOut; +} + +Converter::Options BinaryJsonConverter::outputOptions() +{ + return {}; +} + +const char *BinaryJsonConverter::optionsHelp() +{ + return nullptr; +} + +bool BinaryJsonConverter::probeFile(QIODevice *f) +{ + return f->isReadable() && f->peek(4) == "qbjs"; +} + +QVariant BinaryJsonConverter::loadFile(QIODevice *f, Converter *&outputConverter) +{ + if (!outputConverter) + outputConverter = &jsonConverter; + + QJsonDocument doc; + if (auto file = qobject_cast(f)) { + uchar *ptr = file->map(0, file->size()); + if (ptr) + doc = QJsonDocument::fromRawData(reinterpret_cast(ptr), file->size()); + } + + if (doc.isNull()) + doc = QJsonDocument::fromBinaryData(f->readAll()); + + if (!doc.isObject() && !doc.isArray()) { + fprintf(stderr, "Failed to load Binary JSON.\n"); + exit(EXIT_FAILURE); + } + if (outputConverter == null) + return QVariant(); + return doc.toVariant(); +} + +void BinaryJsonConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) +{ + if (!options.isEmpty()) { + fprintf(stderr, "Unknown option '%s' to JSON output. This format has no options.\n", qPrintable(options.first())); + exit(EXIT_FAILURE); + } + + f->write(convertFromVariant(contents).toBinaryData()); +} diff --git a/examples/corelib/serialization/convert/jsonconverter.h b/examples/corelib/serialization/convert/jsonconverter.h new file mode 100644 index 0000000000..17170603c7 --- /dev/null +++ b/examples/corelib/serialization/convert/jsonconverter.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef JSONCONVERTER_H +#define JSONCONVERTER_H + +#include "converter.h" + +class JsonConverter : public Converter +{ +public: + JsonConverter(); + + // Converter interface +public: + QString name() override; + Direction directions() override; + Options outputOptions() override; + const char *optionsHelp() override; + bool probeFile(QIODevice *f) override; + QVariant loadFile(QIODevice *f, Converter *&outputConverter) override; + void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override; +}; + +class BinaryJsonConverter : public Converter +{ + // Converter interface +public: + QString name() override; + Direction directions() override; + Options outputOptions() override; + const char *optionsHelp() override; + bool probeFile(QIODevice *f) override; + QVariant loadFile(QIODevice *f, Converter *&outputConverter) override; + void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override; +}; + +#endif // JSONCONVERTER_H diff --git a/examples/corelib/serialization/convert/main.cpp b/examples/corelib/serialization/convert/main.cpp new file mode 100644 index 0000000000..e9d14792b0 --- /dev/null +++ b/examples/corelib/serialization/convert/main.cpp @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "converter.h" + +#include +#include +#include +#include +#include + +#include + +static QVector *availableConverters; + +Converter::Converter() +{ + if (!availableConverters) + availableConverters = new QVector; + availableConverters->append(this); +} + +Converter::~Converter() +{ + availableConverters->removeAll(this); +} + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + + QStringList inputFormats; + QStringList outputFormats; + for (Converter *conv : qAsConst(*availableConverters)) { + auto direction = conv->directions(); + QString name = conv->name(); + if (direction & Converter::In) + inputFormats << name; + if (direction & Converter::Out) + outputFormats << name; + } + inputFormats.sort(); + outputFormats.sort(); + inputFormats.prepend("auto"); + outputFormats.prepend("auto"); + + QCommandLineParser parser; + parser.setApplicationDescription(QStringLiteral("Qt file format conversion tool")); + parser.addHelpOption(); + + QCommandLineOption inputFormatOption(QStringList{"I", "input-format"}); + inputFormatOption.setDescription(QLatin1String("Select the input format for the input file. Available formats: ") + + inputFormats.join(", ")); + inputFormatOption.setValueName("format"); + inputFormatOption.setDefaultValue(inputFormats.constFirst()); + parser.addOption(inputFormatOption); + + QCommandLineOption outputFormatOption(QStringList{"O", "output-format"}); + outputFormatOption.setDescription(QLatin1String("Select the output format for the output file. Available formats: ") + + outputFormats.join(", ")); + outputFormatOption.setValueName("format"); + outputFormatOption.setDefaultValue(outputFormats.constFirst()); + parser.addOption(outputFormatOption); + + QCommandLineOption optionOption(QStringList{"o", "option"}); + optionOption.setDescription(QStringLiteral("Format-specific options. Use --format-options to find out what options are available.")); + optionOption.setValueName("options..."); + optionOption.setDefaultValues({}); + parser.addOption(optionOption); + + QCommandLineOption formatOptionsOption("format-options"); + formatOptionsOption.setDescription(QStringLiteral("Prints the list of valid options for --option for the converter format .")); + formatOptionsOption.setValueName("format"); + parser.addOption(formatOptionsOption); + + parser.addPositionalArgument(QStringLiteral("[source]"), + QStringLiteral("File to read from (stdin if none)")); + parser.addPositionalArgument(QStringLiteral("[destination]"), + QStringLiteral("File to write to (stdout if none)")); + + parser.process(app); + + if (parser.isSet(formatOptionsOption)) { + QString format = parser.value(formatOptionsOption); + for (Converter *conv : qAsConst(*availableConverters)) { + if (conv->name() == format) { + const char *help = conv->optionsHelp(); + if (help) + printf("The following options are available for format '%s':\n\n%s", qPrintable(format), help); + else + printf("Format '%s' supports no options.\n", qPrintable(format)); + return EXIT_SUCCESS; + } + } + + fprintf(stderr, "Unknown file format '%s'\n", qPrintable(format)); + return EXIT_FAILURE; + } + + Converter *inconv = nullptr; + QString format = parser.value(inputFormatOption); + if (format != "auto") { + for (Converter *conv : qAsConst(*availableConverters)) { + if (conv->name() == format) { + inconv = conv; + break; + } + } + + if (!inconv) { + fprintf(stderr, "Unknown file format \"%s\"\n", qPrintable(format)); + return EXIT_FAILURE; + } + } + + Converter *outconv = nullptr; + format = parser.value(outputFormatOption); + if (format != "auto") { + for (Converter *conv : qAsConst(*availableConverters)) { + if (conv->name() == format) { + outconv = conv; + break; + } + } + + if (!outconv) { + fprintf(stderr, "Unknown file format \"%s\"\n", qPrintable(format)); + return EXIT_FAILURE; + } + } + + QStringList files = parser.positionalArguments(); + QFile input(files.value(0)); + QFile output(files.value(1)); + + if (input.fileName().isEmpty()) + input.open(stdin, QIODevice::ReadOnly); + else + input.open(QIODevice::ReadOnly); + if (!input.isOpen()) { + fprintf(stderr, "Could not open \"%s\" for reading: %s\n", + qPrintable(input.fileName()), qPrintable(input.errorString())); + return EXIT_FAILURE; + } + + if (output.fileName().isEmpty()) + output.open(stdout, QIODevice::WriteOnly | QIODevice::Truncate); + else + output.open(QIODevice::WriteOnly | QIODevice::Truncate); + if (!output.isOpen()) { + fprintf(stderr, "Could not open \"%s\" for writing: %s\n", + qPrintable(output.fileName()), qPrintable(output.errorString())); + return EXIT_FAILURE; + } + + if (!inconv) { + // probe the input to find a file format + for (Converter *conv : qAsConst(*availableConverters)) { + if (conv->directions() & Converter::In && conv->probeFile(&input)) { + inconv = conv; + break; + } + } + + if (!inconv) { + fprintf(stderr, "Could not determine input format. pass -I option.\n"); + return EXIT_FAILURE; + } + } + + if (!outconv) { + // probe the output to find a file format + for (Converter *conv : qAsConst(*availableConverters)) { + if (conv->directions() & Converter::Out && conv->probeFile(&output)) { + outconv = conv; + break; + } + } + } + + // now finally perform the conversion + QVariant data = inconv->loadFile(&input, outconv); + Q_ASSERT_X(outconv, "Converter Tool", + "Internal error: converter format did not provide default"); + outconv->saveFile(&output, data, parser.values(optionOption)); + return EXIT_SUCCESS; +} diff --git a/examples/corelib/serialization/convert/nullconverter.cpp b/examples/corelib/serialization/convert/nullconverter.cpp new file mode 100644 index 0000000000..2de492e64e --- /dev/null +++ b/examples/corelib/serialization/convert/nullconverter.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "nullconverter.h" + +static NullConverter nullConverter; +Converter* Converter::null = &nullConverter; + +QString NullConverter::name() +{ + return QLatin1String("null"); +} + +Converter::Direction NullConverter::directions() +{ + return Out; +} + +Converter::Options NullConverter::outputOptions() +{ + return SupportsArbitraryMapKeys; +} + +const char *NullConverter::optionsHelp() +{ + return nullptr; +} + +bool NullConverter::probeFile(QIODevice *f) +{ + Q_UNUSED(f); + return false; +} + +QVariant NullConverter::loadFile(QIODevice *f, Converter *&outputConverter) +{ + Q_UNUSED(f); + Q_UNUSED(outputConverter); + outputConverter = this; + return QVariant(); +} + +void NullConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) +{ + if (!options.isEmpty()) { + fprintf(stderr, "Unknown option '%s' to null output. This format has no options.\n", qPrintable(options.first())); + exit(EXIT_FAILURE); + } + + Q_UNUSED(f); + Q_UNUSED(contents); +} diff --git a/examples/corelib/serialization/convert/nullconverter.h b/examples/corelib/serialization/convert/nullconverter.h new file mode 100644 index 0000000000..af7339f092 --- /dev/null +++ b/examples/corelib/serialization/convert/nullconverter.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NULLCONVERTER_H +#define NULLCONVERTER_H + +#include "converter.h" + +class NullConverter : public Converter +{ + // Converter interface +public: + QString name() override; + Direction directions() override; + Options outputOptions() override; + const char *optionsHelp() override; + bool probeFile(QIODevice *f) override; + QVariant loadFile(QIODevice *f, Converter *&outputConverter) override; + void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override; +}; + +#endif // NULLCONVERTER_H diff --git a/examples/corelib/serialization/convert/textconverter.cpp b/examples/corelib/serialization/convert/textconverter.cpp new file mode 100644 index 0000000000..e80e69a0b5 --- /dev/null +++ b/examples/corelib/serialization/convert/textconverter.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "textconverter.h" + +#include +#include + +static void dumpVariant(QTextStream &out, const QVariant &v) +{ + switch (v.userType()) { + case QVariant::List: { + const QVariantList list = v.toList(); + for (const QVariant &item : list) + dumpVariant(out, item); + break; + } + + case QVariant::String: { + const QStringList list = v.toStringList(); + for (const QString &s : list) + out << s << endl; + break; + } + + case QVariant::Map: { + const QVariantMap map = v.toMap(); + for (auto it = map.begin(); it != map.end(); ++it) { + out << it.key() << " => "; + dumpVariant(out, it.value()); + } + break; + } + + case QMetaType::Nullptr: + out << "(null)" << endl; + break; + + default: + out << v.toString() << endl; + break; + } +} + +QString TextConverter::name() +{ + return QStringLiteral("text"); +} + +Converter::Direction TextConverter::directions() +{ + return InOut; +} + +Converter::Options TextConverter::outputOptions() +{ + return {}; +} + +const char *TextConverter::optionsHelp() +{ + return nullptr; +} + +bool TextConverter::probeFile(QIODevice *f) +{ + if (QFile *file = qobject_cast(f)) + return file->fileName().endsWith(QLatin1String(".txt")); + return false; +} + +QVariant TextConverter::loadFile(QIODevice *f, Converter *&outputConverter) +{ + if (!outputConverter) + outputConverter = this; + + QVariantList list; + QTextStream in(f); + QString line ; + while (!in.atEnd()) { + in.readLineInto(&line); + + bool ok; + qint64 v = line.toLongLong(&ok); + if (ok) { + list.append(v); + continue; + } + + double d = line.toDouble(&ok); + if (ok) { + list.append(d); + continue; + } + + list.append(line); + } + + return list; +} + +void TextConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) +{ + if (!options.isEmpty()) { + fprintf(stderr, "Unknown option '%s' to text output. This format has no options.\n", qPrintable(options.first())); + exit(EXIT_FAILURE); + } + + QTextStream out(f); + dumpVariant(out, contents); +} + +static TextConverter textConverter; diff --git a/examples/corelib/serialization/convert/textconverter.h b/examples/corelib/serialization/convert/textconverter.h new file mode 100644 index 0000000000..66f5136c02 --- /dev/null +++ b/examples/corelib/serialization/convert/textconverter.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TEXTCONVERTER_H +#define TEXTCONVERTER_H + +#include "converter.h" + +class TextConverter : public Converter +{ + + // Converter interface +public: + QString name() override; + Direction directions() override; + Options outputOptions() override; + const char *optionsHelp() override; + bool probeFile(QIODevice *f) override; + QVariant loadFile(QIODevice *f, Converter *&outputConverter) override; + void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override; +}; + +#endif // TEXTCONVERTER_H diff --git a/examples/corelib/serialization/convert/xmlconverter.cpp b/examples/corelib/serialization/convert/xmlconverter.cpp new file mode 100644 index 0000000000..62908273ce --- /dev/null +++ b/examples/corelib/serialization/convert/xmlconverter.cpp @@ -0,0 +1,514 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "xmlconverter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char optionHelp[] = + "compact=no|yes Use compact XML form.\n"; + +static XmlConverter xmlConverter; + +static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options); + +static QVariantList listFromXml(QXmlStreamReader &xml, Converter::Options options) +{ + QVariantList list; + while (!xml.atEnd() && !xml.isEndElement()) { + xml.readNext(); + switch (xml.tokenType()) { + case QXmlStreamReader::StartElement: + list << variantFromXml(xml, options); + continue; + + case QXmlStreamReader::EndElement: + continue; + + case QXmlStreamReader::Comment: + // ignore comments + continue; + + case QXmlStreamReader::Characters: + // ignore whitespace + if (xml.isWhitespace()) + continue; + Q_FALLTHROUGH(); + + default: + break; + } + + fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", + xml.lineNumber(), xml.columnNumber(), + qPrintable(xml.tokenString()), qPrintable(xml.name().toString())); + exit(EXIT_FAILURE); + } + + xml.readNext(); + return list; +} + +static VariantOrderedMap::value_type mapEntryFromXml(QXmlStreamReader &xml, Converter::Options options) +{ + QVariant key, value; + while (!xml.atEnd() && !xml.isEndElement()) { + xml.readNext(); + switch (xml.tokenType()) { + case QXmlStreamReader::StartElement: + if (value.isValid()) + break; + if (key.isValid()) + value = variantFromXml(xml, options); + else + key = variantFromXml(xml, options); + continue; + + case QXmlStreamReader::EndElement: + continue; + + case QXmlStreamReader::Comment: + // ignore comments + continue; + + case QXmlStreamReader::Characters: + // ignore whitespace + if (xml.isWhitespace()) + continue; + Q_FALLTHROUGH(); + + default: + break; + } + + fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", + xml.lineNumber(), xml.columnNumber(), + qPrintable(xml.tokenString()), qPrintable(xml.name().toString())); + exit(EXIT_FAILURE); + } + + return { key, value }; +} + +static QVariant mapFromXml(QXmlStreamReader &xml, Converter::Options options) +{ + QVariantMap map1; + VariantOrderedMap map2; + + while (!xml.atEnd() && !xml.isEndElement()) { + xml.readNext(); + switch (xml.tokenType()) { + case QXmlStreamReader::StartElement: + if (xml.name() == QLatin1String("entry")) { + auto pair = mapEntryFromXml(xml, options); + if (options & Converter::SupportsArbitraryMapKeys) + map2.append(pair); + else + map1.insert(pair.first.toString(), pair.second); + continue; + } + break; + + case QXmlStreamReader::EndElement: + continue; + + case QXmlStreamReader::Comment: + // ignore comments + continue; + + case QXmlStreamReader::Characters: + // ignore whitespace + if (xml.isWhitespace()) + continue; + Q_FALLTHROUGH(); + + default: + break; + } + + fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", + xml.lineNumber(), xml.columnNumber(), + qPrintable(xml.tokenString()), qPrintable(xml.name().toString())); + exit(EXIT_FAILURE); + } + + xml.readNext(); + if (options & Converter::SupportsArbitraryMapKeys) + return QVariant::fromValue(map2); + return map1; +} + +static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options) +{ + QStringRef name = xml.name(); + if (name == QLatin1String("list")) + return listFromXml(xml, options); + if (name == QLatin1String("map")) + return mapFromXml(xml, options); + if (name != QLatin1String("value")) { + fprintf(stderr, "%lld:%lld: Invalid XML key '%s'.\n", + xml.lineNumber(), xml.columnNumber(), qPrintable(name.toString())); + exit(EXIT_FAILURE); + } + + QXmlStreamAttributes attrs = xml.attributes(); + QStringRef type = attrs.value(QLatin1String("type")); + + forever { + xml.readNext(); + if (xml.isComment()) + continue; + if (xml.isCDATA() || xml.isCharacters() || xml.isEndElement()) + break; + + fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", + xml.lineNumber(), xml.columnNumber(), + qPrintable(xml.tokenString()), qPrintable(name.toString())); + exit(EXIT_FAILURE); + } + + QStringRef text = xml.text(); + if (!xml.isCDATA()) + text = text.trimmed(); + + QVariant result; + bool ok; + if (type.isEmpty()) { + // ok + } else if (type == QLatin1String("number")) { + // try integer first + qint64 v = text.toLongLong(&ok); + if (ok) { + result = v; + } else { + // let's see floating point + double d = text.toDouble(&ok); + result = d; + if (!ok) { + fprintf(stderr, "%lld:%lld: Invalid XML: could not interpret '%s' as a number.\n", + xml.lineNumber(), xml.columnNumber(), qPrintable(text.toString())); + exit(EXIT_FAILURE); + } + } + } else if (type == QLatin1String("bytes")) { + QByteArray data = text.toLatin1(); + QStringRef encoding = attrs.value("encoding"); + if (encoding == QLatin1String("base64url")) { + result = QByteArray::fromBase64(data, QByteArray::Base64UrlEncoding); + } else if (encoding == QLatin1String("hex")) { + result = QByteArray::fromHex(data); + } else if (encoding.isEmpty() || encoding == QLatin1String("base64")) { + result = QByteArray::fromBase64(data); + } else { + fprintf(stderr, "%lld:%lld: Invalid XML: unknown encoding '%s' for bytes.\n", + xml.lineNumber(), xml.columnNumber(), qPrintable(encoding.toString())); + exit(EXIT_FAILURE); + } + } else if (type == QLatin1String("string")) { + result = text.toString(); + } else if (type == QLatin1String("null")) { + result = QVariant::fromValue(nullptr); + } else if (type == QLatin1String("CBOR simple type")) { + result = QVariant::fromValue(QCborSimpleType(text.toShort())); + } else if (type == QLatin1String("bits")) { + QBitArray ba; + ba.resize(text.size()); + qsizetype n = 0; + for (qsizetype i = 0; i < text.size(); ++i) { + QChar c = text.at(i); + if (c == '1') { + ba.setBit(n++); + } else if (c == '0') { + ++n; + } else if (!c.isSpace()) { + fprintf(stderr, "%lld:%lld: Invalid XML: invalid bit string '%s'.\n", + xml.lineNumber(), xml.columnNumber(), qPrintable(text.toString())); + exit(EXIT_FAILURE); + } + } + ba.resize(n); + result = ba; + } else { + int id = QVariant::Invalid; + if (type == QLatin1String("datetime")) + id = QVariant::DateTime; + else if (type == QLatin1String("url")) + id = QVariant::Url; + else if (type == QLatin1String("uuid")) + id = QVariant::Uuid; + else if (type == QLatin1String("regex")) + id = QVariant::RegularExpression; + else + id = QMetaType::type(type.toLatin1()); + if (id == QVariant::Invalid) { + fprintf(stderr, "%lld:%lld: Invalid XML: unknown type '%s'.\n", + xml.lineNumber(), xml.columnNumber(), qPrintable(type.toString())); + exit(EXIT_FAILURE); + } + + result = text.toString(); + if (!result.convert(id)) { + fprintf(stderr, "%lld:%lld: Invalid XML: could not parse content as type '%s'.\n", + xml.lineNumber(), xml.columnNumber(), qPrintable(type.toString())); + exit(EXIT_FAILURE); + } + } + + do { + xml.readNext(); + } while (xml.isComment() || xml.isWhitespace()); + + if (!xml.isEndElement()) { + fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", + xml.lineNumber(), xml.columnNumber(), + qPrintable(xml.tokenString()), qPrintable(name.toString())); + exit(EXIT_FAILURE); + } + + xml.readNext(); + return result; +} + +static void variantToXml(QXmlStreamWriter &xml, const QVariant &v) +{ + int type = v.userType(); + if (type == QVariant::List) { + QVariantList list = v.toList(); + xml.writeStartElement("list"); + for (const QVariant &v : list) + variantToXml(xml, v); + xml.writeEndElement(); + } else if (type == QVariant::Map || type == qMetaTypeId()) { + const VariantOrderedMap map = (type == QVariant::Map) ? + VariantOrderedMap(v.toMap()) : + v.value(); + + xml.writeStartElement("map"); + for (const auto &pair : map) { + xml.writeStartElement("entry"); + variantToXml(xml, pair.first); + variantToXml(xml, pair.second); + xml.writeEndElement(); + } + xml.writeEndElement(); + } else { + xml.writeStartElement("value"); + QString typeString = QStringLiteral("type"); + switch (type) { + case QMetaType::Short: + case QMetaType::UShort: + case QMetaType::Int: + case QMetaType::UInt: + case QMetaType::Long: + case QMetaType::ULong: + case QMetaType::LongLong: + case QMetaType::ULongLong: + case QMetaType::Float: + case QMetaType::Double: + xml.writeAttribute(typeString, "number"); + xml.writeCharacters(v.toString()); + break; + + case QMetaType::QByteArray: + xml.writeAttribute(typeString, "bytes"); + xml.writeAttribute("encoding", "base64"); + xml.writeCharacters(QString::fromLatin1(v.toByteArray().toBase64())); + break; + + case QMetaType::QString: + xml.writeAttribute(typeString, "string"); + xml.writeCDATA(v.toString()); + break; + + case QMetaType::Bool: + xml.writeAttribute(typeString, "bool"); + xml.writeCharacters(v.toString()); + break; + + case QMetaType::Nullptr: + xml.writeAttribute(typeString, "null"); + break; + + case QMetaType::UnknownType: + break; + + case QMetaType::QDate: + case QMetaType::QTime: + case QMetaType::QDateTime: + xml.writeAttribute(typeString, "dateime"); + xml.writeCharacters(v.toString()); + break; + + case QMetaType::QUrl: + xml.writeAttribute(typeString, "url"); + xml.writeCharacters(v.toUrl().toString(QUrl::FullyEncoded)); + break; + + case QMetaType::QUuid: + xml.writeAttribute(typeString, "uuid"); + xml.writeCharacters(v.toString()); + break; + + case QMetaType::QBitArray: + xml.writeAttribute(typeString, "bits"); + xml.writeCharacters([](const QBitArray &ba) { + QString result; + for (qsizetype i = 0; i < ba.size(); ++i) { + if (i && i % 72 == 0) + result += '\n'; + result += QLatin1Char(ba.testBit(i) ? '1' : '0'); + } + return result; + }(v.toBitArray())); + break; + + case QMetaType::QRegularExpression: + xml.writeAttribute(typeString, "regex"); + xml.writeCharacters(v.toRegularExpression().pattern()); + break; + + default: + if (type == qMetaTypeId()) { + xml.writeAttribute(typeString, "number"); + xml.writeCharacters(QString::number(float(v.value()))); + } else if (type == qMetaTypeId()) { + xml.writeAttribute(typeString, "CBOR simple type"); + xml.writeCharacters(QString::number(int(v.value()))); + } else { + // does this convert to string? + const char *typeName = v.typeName(); + QVariant copy = v; + if (copy.convert(QVariant::String)) { + xml.writeAttribute(typeString, QString::fromLatin1(typeName)); + xml.writeCharacters(copy.toString()); + } else { + fprintf(stderr, "XML: don't know how to serialize type '%s'.\n", typeName); + exit(EXIT_FAILURE); + } + } + } + xml.writeEndElement(); + } +} + +QString XmlConverter::name() +{ + return QStringLiteral("xml"); +} + +Converter::Direction XmlConverter::directions() +{ + return InOut; +} + +Converter::Options XmlConverter::outputOptions() +{ + return SupportsArbitraryMapKeys; +} + +const char *XmlConverter::optionsHelp() +{ + return optionHelp; +} + +bool XmlConverter::probeFile(QIODevice *f) +{ + if (QFile *file = qobject_cast(f)) { + if (file->fileName().endsWith(QLatin1String(".xml"))) + return true; + } + + return f->isReadable() && f->peek(5) == "outputOptions()); + if (xml.hasError()) { + fprintf(stderr, "XML error: %s", qPrintable(xml.errorString())); + exit(EXIT_FAILURE); + } + + return v; +} + +void XmlConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) +{ + bool compact = false; + for (const QString &s : options) { + if (s == QLatin1String("compact=no")) { + compact = false; + } else if (s == QLatin1String("compact=yes")) { + compact = true; + } else { + fprintf(stderr, "Unknown option '%s' to XML output. Valid options are:\n%s", qPrintable(s), optionHelp); + exit(EXIT_FAILURE); + } + } + + QXmlStreamWriter xml(f); + xml.setAutoFormatting(!compact); + xml.writeStartDocument(); + variantToXml(xml, contents); + xml.writeEndDocument(); +} diff --git a/examples/corelib/serialization/convert/xmlconverter.h b/examples/corelib/serialization/convert/xmlconverter.h new file mode 100644 index 0000000000..8fc0fea592 --- /dev/null +++ b/examples/corelib/serialization/convert/xmlconverter.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef XMLCONVERTER_H +#define XMLCONVERTER_H + +#include "converter.h" + +class XmlConverter : public Converter +{ + // Converter interface +public: + QString name() override; + Direction directions() override; + Options outputOptions() override; + const char *optionsHelp() override; + bool probeFile(QIODevice *f) override; + QVariant loadFile(QIODevice *f, Converter *&outputConverter) override; + void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override; +}; + +#endif // XMLCONVERTER_H diff --git a/examples/corelib/serialization/serialization.pro b/examples/corelib/serialization/serialization.pro index 34fea0c11a..7651444f19 100644 --- a/examples/corelib/serialization/serialization.pro +++ b/examples/corelib/serialization/serialization.pro @@ -1,4 +1,5 @@ TEMPLATE = subdirs SUBDIRS = \ cbordump \ + convert \ savegame -- cgit v1.2.3