diff options
Diffstat (limited to 'examples/corelib/serialization')
53 files changed, 2268 insertions, 2061 deletions
diff --git a/examples/corelib/serialization/CMakeLists.txt b/examples/corelib/serialization/CMakeLists.txt index 486f5d0c14..e32e4df441 100644 --- a/examples/corelib/serialization/CMakeLists.txt +++ b/examples/corelib/serialization/CMakeLists.txt @@ -1,5 +1,11 @@ -# Generated from serialization.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -qt_internal_add_example(cbordump) -qt_internal_add_example(convert) -qt_internal_add_example(savegame) +if(NOT ANDROID) + qt_internal_add_example(cbordump) + qt_internal_add_example(convert) + qt_internal_add_example(savegame) +endif() +if(TARGET Qt6::Widgets) + qt_internal_add_example(streambookmarks) +endif() diff --git a/examples/corelib/serialization/cbordump/CMakeLists.txt b/examples/corelib/serialization/cbordump/CMakeLists.txt index 106df4b24d..b2c3a536e3 100644 --- a/examples/corelib/serialization/cbordump/CMakeLists.txt +++ b/examples/corelib/serialization/cbordump/CMakeLists.txt @@ -1,35 +1,34 @@ -# Generated from cbordump.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause cmake_minimum_required(VERSION 3.16) project(cbordump LANGUAGES CXX) -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) -set(CMAKE_AUTOUIC ON) - -if(NOT DEFINED INSTALL_EXAMPLESDIR) - set(INSTALL_EXAMPLESDIR "examples") +if (ANDROID) + message(FATAL_ERROR "This project cannot be built on Android.") endif() -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/serialization/cbordump") +find_package(Qt6 REQUIRED COMPONENTS Core) -find_package(Qt6 COMPONENTS Core) +qt_standard_project_setup() qt_add_executable(cbordump main.cpp ) -set_target_properties(cbordump PROPERTIES - WIN32_EXECUTABLE FALSE - MACOSX_BUNDLE FALSE -) -target_link_libraries(cbordump PUBLIC - Qt::Core + +target_link_libraries(cbordump PRIVATE + Qt6::Core ) install(TARGETS cbordump - RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" - BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" - LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_app_script( + TARGET cbordump + OUTPUT_SCRIPT deploy_script + NO_UNSUPPORTED_PLATFORM_ERROR ) +install(SCRIPT ${deploy_script}) diff --git a/examples/corelib/serialization/cbordump/cbortag.py b/examples/corelib/serialization/cbordump/cbortag.py new file mode 100755 index 0000000000..26a0f969e4 --- /dev/null +++ b/examples/corelib/serialization/cbordump/cbortag.py @@ -0,0 +1,188 @@ +#!/bin/env python3 +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +"""Digest cbor-tags.xml file into code for insertion into main.cpp + +See main.cpp's comment on how to regenerate its GENERATED CODE. +See ./cbortag.py --help for further details on how to invoke. +You can import this is a module without invoking the script. +""" + +def firstChild(parent, tag): + """Return parent's first child element with the given tag.""" + return next(node for node in parent.childNodes + if node.nodeType == parent.ELEMENT_NODE and node.nodeName == tag) + +def nodeAttrIs(node, attr, seek): + """Checks whether the node has a given value for an attribute + + Takes the node to check, the name of the attribute and the value + to check against. Returns true if the node does have that value + for the named attribute.""" + if node.nodeType != node.ELEMENT_NODE: + return False + if node.attributes is None or attr not in node.attributes: + return False + return node.attributes[attr].value == seek + +def getRfcValue(node): + """Extract RFC reference from an <xref type="rfc" ...> element + + Some of these have a reference including section details as the + body of the element, otherwise the data attribute should identify + the RFC. If neither is found, an empty string is returned.""" + if node.childNodes: + return node.childNodes[0].nodeValue # Maybe accumulate several children ? + if node.attributes is None or 'data' not in node.attributes: + return '' + return node.attributes['data'].value + +def readRegistry(filename): + """Handles the XML parsing and returns the relevant parts. + + Single argument is the path to the cbor-tags.xml file; returns a + twople of the title element's text and an interator over the + record nodes. Checks some things are as expected while doing so.""" + from xml.dom.minidom import parse + doc = parse(filename).documentElement + assert nodeAttrIs(doc, 'id', 'cbor-tags') + title = firstChild(doc, 'title').childNodes[0].nodeValue + registry = firstChild(doc, 'registry') + assert nodeAttrIs(registry, 'id', 'tags') + records = (node for node in registry.childNodes if node.nodeName == 'record') + return title, records + +def digest(record): + """Digest a single record from cbor-tags.xml + + If the record is not of interest, returns the twople (None, None). + For records of interest, returns (n, t) where n is the numeric tag + code of the record and t is a text describing it. If the record, + or its semantics field, has an xref child with type="rfc", the RFC + mentioned there is included with the text of the semantics; such a + record is of interest, provided it has a semantics field and no + dash in its value. Records with a value field containing a dash + (indicating a range) are not of interest. Records with a value of + 256 or above are only of interest if they include an RFC.""" + data = {} + for kid in record.childNodes: + if kid.nodeName == 'xref': + if not nodeAttrIs(kid, 'type', 'rfc'): + continue + rfc = getRfcValue(kid) + if rfc: + # Potentially stomping one taken from semantics + data['rfc'] = rfc + elif kid.nodeName == 'semantics': + text = rfc = '' + for part in kid.childNodes: + if part.nodeType == kid.TEXT_NODE: + text += part.nodeValue + elif part.nodeType == kid.ELEMENT_NODE: + if part.nodeName != 'xref' or not nodeAttrIs(part, 'type', 'rfc'): + continue # potentially append content to text + assert not rfc, ('Duplicate RFC ?', rfc, part) + rfc = getRfcValue(part) + if rfc: + if text.endswith('()'): + text = text[:-2].rstrip() + if 'rfc' not in data: + data['rfc'] = rfc + data['semantics'] = ' '.join(text.split()) + elif kid.nodeName == 'value': + data['value'] = kid.childNodes[0].nodeValue + text = data.get('semantics') + if not text or 'value' not in data or '-' in data['value']: + return None, None + value = int(data['value']) + if 'rfc' in data: + rfc = data["rfc"].replace('rfc', 'RFC') + text = f'{text} [{rfc}]' + elif value >= 256: + return None, None + return value, text + +def entries(records): + """Digest each record of interest into a value and text. + + The value and text form the raw material of the tagDescriptions + array in main.cpp; see digest for which records are retained.""" + for record in records: + value, text = digest(record) + if value is not None: + yield value, text + +def marginBound(text, prior, left, right): + """Split up a string literal for tidy display. + + The first parameter, text, is the content of the string literal; + quotes shall be added. It may be split into several fragments, + each quoted, so as to abide by line length constraints. + + The remaining parameters are integers: prior is the text already + present on the line before text is to be added; left is the width + of the left margin for all subsequent lines; and right is the + right margin to stay within, where possible. The returned string + is either a space with the whole quoted text following, to fit on + the line already started to length prior, or a sequence of quoted + strings, each preceded by a newline and indent of width left.""" + if prior + 3 + len(text) < right: # 1 for space, 2 for quotes + return f' "{text}"' + width = right - left - 2 # 2 for the quotes + words = iter(text.split(' ')) + lines, current = [''], [next(words)] + for word in words: + if len(word) + sum(len(w) + 1 for w in current) > width: + line = ' '.join(current) + lines.append(f'"{line}"') + current = ['', word] + else: + current.append(word) + line = ' '.join(current) + lines.append(f'"{line}"') + return ('\n' + ' ' * left).join(lines) + +def main(argv, speak): + """Takes care of driving the process. + + Takes the command-line argument list (whose first entry is the + name of this script) and standard output (or compatible stream of + your choosing) to which to write data. If the --out option is + specified in the arguments, the file it names is used in place of + this output stream.""" + from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter + parser = ArgumentParser( + description='Digest cbor-tags.xml into code to insert in main.cpp', + formatter_class=ArgumentDefaultsHelpFormatter) + parser.add_argument('path', help='path of the cbor-tags.xml file', + default='cbor-tags.xml') + parser.add_argument('--out', help='file to write instead of standard output') + args = parser.parse_args(argv[1:]) + emit = (open(args.out) if args.out else speak).write + + title, records = readRegistry(args.path) + emit(f"""\ +struct CborTagDescription +{{ + QCborTag tag; + const char *description; // with space and parentheses +}}; + +// {title} +static const CborTagDescription tagDescriptions[] = {{ + // from https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml +""") + + for value, text in sorted(entries(records)): + prior = f' {{ QCborTag({value}),' + body = marginBound(f' ({text})', len(prior), 6, 96) + emit(f"{prior}{body} }},\n") + + emit("""\ + { QCborTag(-1), nullptr } +}; +""") + +if __name__ == '__main__': + import sys + sys.exit(main(sys.argv, sys.stdout)) diff --git a/examples/corelib/serialization/cbordump/doc/images/cbordump.png b/examples/corelib/serialization/cbordump/doc/images/cbordump.png Binary files differnew file mode 100644 index 0000000000..d5ff1da0bf --- /dev/null +++ b/examples/corelib/serialization/cbordump/doc/images/cbordump.png diff --git a/examples/corelib/serialization/cbordump/doc/src/cbordump.qdoc b/examples/corelib/serialization/cbordump/doc/src/cbordump.qdoc new file mode 100644 index 0000000000..a4dc01116f --- /dev/null +++ b/examples/corelib/serialization/cbordump/doc/src/cbordump.qdoc @@ -0,0 +1,55 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \example serialization/cbordump + \examplecategory {Data Processing & I/O} + \meta tag {network} + \title Parsing and displaying CBOR data + + \brief A demonstration of how to parse files in CBOR format. + + This example shows how to use the QCborStreamReader class directly to parse + CBOR content. The \c cbordump program reads content in CBOR format from + files or standard input and dumps the decoded content to stdout in a + human-readable format. It can output in CBOR diagnostic notation (which is + similar to JSON), or it can produce a verbose output where each byte input + is displayed with its encoding beside it. + + \sa QCborStreamReader + + \image cbordump.png + + \section1 The CborDumper Class + + The CborDumper class contains a QCborStreamReader object that is initialized + using the QFile object argument passed to the CborDumper constructor. Based + on the arguments the dump function calls either dumpOne() or + dumpOneDetailed() to dump the contents to standard output, + + \snippet serialization/cbordump/main.cpp 0 + + \section2 The dumpOne() Function + + Switching on QCborStreamReader::type() enables printing appropriate to the + type of the current value in the stream. If the type is an array or map, the + value's content is iterated over, and for each entry the dumpOne() function + is called recursively with a higher indentation argument. If the type is a + tag, it is printed out and dumpOne() is called once without increasing the + indentation argument. + + \section2 The dumpOneDetailed() Function + + This function dumps out both the incoming bytes and the decoded contents + on the same line. It uses lambda functions to print out the bytes and + decoded content, but otherwise has a similar structure as dumpOne(). + + \section1 CborTagDescription + + The \c tagDescriptions table, describing the CBOR tags available, is + automatically generated from an XML file available from the iana.org + website. When \c dumpOneDetailed() reports a tag, it uses its description + from this table. + + \sa {CBOR Support in Qt} +*/ diff --git a/examples/corelib/serialization/cbordump/main.cpp b/examples/corelib/serialization/cbordump/main.cpp index 928bc024c5..03c940452e 100644 --- a/examples/corelib/serialization/cbordump/main.cpp +++ b/examples/corelib/serialization/cbordump/main.cpp @@ -1,52 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include <QCborStreamReader> #include <QCommandLineParser> @@ -61,86 +14,137 @@ #include <stdarg.h> #include <stdio.h> +using namespace Qt::StringLiterals; + /* * To regenerate: * curl -O https://www.iana.org/assignments/cbor-tags/cbor-tags.xml - * xsltproc tag-transform.xslt cbor-tags.xml + * ./cbortag.py cbor-tags.xml + * + * The XHTML URL mentioned in the comment below is a human-readable version of + * the same resource. */ // GENERATED CODE struct CborTagDescription { QCborTag tag; - const char *description; // with space and parentheses + const char *description; // with space and parentheses }; -// CBOR Tags +// Concise Binary Object Representation (CBOR) Tags static const CborTagDescription tagDescriptions[] = { // from https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml - { QCborTag(0), " (Standard date/time string; see Section 2.4.1 [RFC7049])" }, - { QCborTag(1), " (Epoch-based date/time; see Section 2.4.1 [RFC7049])" }, - { QCborTag(2), " (Positive bignum; see Section 2.4.2 [RFC7049])" }, - { QCborTag(3), " (Negative bignum; see Section 2.4.2 [RFC7049])" }, - { QCborTag(4), " (Decimal fraction; see Section 2.4.3 [RFC7049])" }, - { QCborTag(5), " (Bigfloat; see Section 2.4.3 [RFC7049])" }, - { QCborTag(16), " (COSE Single Recipient Encrypted Data Object [RFC8152])" }, - { QCborTag(17), " (COSE Mac w/o Recipients Object [RFC8152])" }, - { QCborTag(18), " (COSE Single Signer Data Object [RFC8152])" }, - { QCborTag(21), " (Expected conversion to base64url encoding; see Section 2.4.4.2 [RFC7049])" }, - { QCborTag(22), " (Expected conversion to base64 encoding; see Section 2.4.4.2 [RFC7049])" }, - { QCborTag(23), " (Expected conversion to base16 encoding; see Section 2.4.4.2 [RFC7049])" }, - { QCborTag(24), " (Encoded CBOR data item; see Section 2.4.4.1 [RFC7049])" }, + { QCborTag(0), " (Standard date/time string; see Section 3.4.1 [RFC8949])" }, + { QCborTag(1), " (Epoch-based date/time; see Section 3.4.2 [RFC8949])" }, + { QCborTag(2), " (Positive bignum; see Section 3.4.3 [RFC8949])" }, + { QCborTag(3), " (Negative bignum; see Section 3.4.3 [RFC8949])" }, + { QCborTag(4), " (Decimal fraction; see Section 3.4.4 [RFC8949])" }, + { QCborTag(5), " (Bigfloat; see Section 3.4.4 [RFC8949])" }, + { QCborTag(16), " (COSE Single Recipient Encrypted Data Object [RFC9052])" }, + { QCborTag(17), " (COSE Mac w/o Recipients Object [RFC9052])" }, + { QCborTag(18), " (COSE Single Signer Data Object [RFC9052])" }, + { QCborTag(19), " (COSE standalone V2 countersignature [RFC9338])" }, + { QCborTag(21), + " (Expected conversion to base64url encoding; see Section 3.4.5.2 [RFC8949])" }, + { QCborTag(22), " (Expected conversion to base64 encoding; see Section 3.4.5.2 [RFC8949])" }, + { QCborTag(23), " (Expected conversion to base16 encoding; see Section 3.4.5.2 [RFC8949])" }, + { QCborTag(24), " (Encoded CBOR data item; see Section 3.4.5.1 [RFC8949])" }, { QCborTag(25), " (reference the nth previously seen string)" }, { QCborTag(26), " (Serialised Perl object with classname and constructor arguments)" }, - { QCborTag(27), " (Serialised language-independent object with type name and constructor arguments)" }, + { QCborTag(27), + " (Serialised language-independent object with type name and constructor arguments)" }, { QCborTag(28), " (mark value as (potentially) shared)" }, { QCborTag(29), " (reference nth marked value)" }, { QCborTag(30), " (Rational number)" }, - { QCborTag(32), " (URI; see Section 2.4.4.3 [RFC7049])" }, - { QCborTag(33), " (base64url; see Section 2.4.4.3 [RFC7049])" }, - { QCborTag(34), " (base64; see Section 2.4.4.3 [RFC7049])" }, + { QCborTag(31), " (Absent value in a CBOR Array)" }, + { QCborTag(32), " (URI; see Section 3.4.5.3 [RFC8949])" }, + { QCborTag(33), " (base64url; see Section 3.4.5.3 [RFC8949])" }, + { QCborTag(34), " (base64; see Section 3.4.5.3 [RFC8949])" }, { QCborTag(35), " (Regular expression; see Section 2.4.4.3 [RFC7049])" }, - { QCborTag(36), " (MIME message; see Section 2.4.4.3 [RFC7049])" }, - { QCborTag(37), " (Binary UUID ( section 4.1.2))" }, - { QCborTag(38), " (Language-tagged string)" }, + { QCborTag(36), " (MIME message; see Section 3.4.5.3 [RFC8949])" }, + { QCborTag(37), " (Binary UUID [RFC4122, Section 4.1.2])" }, + { QCborTag(38), " (Language-tagged string [RFC9290, Appendix A])" }, { QCborTag(39), " (Identifier)" }, - { QCborTag(61), " (CBOR Web Token (CWT))" }, - { QCborTag(96), " (COSE Encrypted Data Object [RFC8152])" }, - { QCborTag(97), " (COSE MACed Data Object [RFC8152])" }, - { QCborTag(98), " (COSE Signed Data Object [RFC8152])" }, - { QCborTag(256), " (mark value as having string references)" }, - { QCborTag(257), " (Binary MIME message)" }, - { QCborTag(258), " (Mathematical finite set)" }, - { QCborTag(260), " (Network Address (IPv4 or IPv6 or MAC Address))" }, - { QCborTag(264), " (Decimal fraction with arbitrary exponent)" }, - { QCborTag(265), " (Bigfloat with arbitrary exponent)" }, - { QCborTag(1001), " (extended time)" }, - { QCborTag(1002), " (duration)" }, - { QCborTag(1003), " (period)" }, - { QCborTag(22098), " (hint that indicates an additional level of indirection)" }, - { QCborTag(55799), " (Self-describe CBOR; see Section 2.4.5 [RFC7049])" }, - { QCborTag(15309736), " (RAINS Message)" }, + { QCborTag(40), " (Multi-dimensional Array, row-major order [RFC8746])" }, + { QCborTag(41), " (Homogeneous Array [RFC8746])" }, + { QCborTag(42), " (IPLD content identifier)" }, + { QCborTag(43), " (YANG bits datatype; see Section 6.7. [RFC9254])" }, + { QCborTag(44), " (YANG enumeration datatype; see Section 6.6. [RFC9254])" }, + { QCborTag(45), " (YANG identityref datatype; see Section 6.10. [RFC9254])" }, + { QCborTag(46), " (YANG instance-identifier datatype; see Section 6.13. [RFC9254])" }, + { QCborTag(47), " (YANG Schema Item iDentifier (sid); see Section 3.2. [RFC9254])" }, + { QCborTag(52), " (IPv4, [prefixlen,IPv4], [IPv4,prefixpart] [RFC9164])" }, + { QCborTag(54), " (IPv6, [prefixlen,IPv6], [IPv6,prefixpart] [RFC9164])" }, + { QCborTag(61), " (CBOR Web Token (CWT) [RFC8392])" }, + { QCborTag(63), " (Encoded CBOR Sequence [RFC8742])" }, + { QCborTag(64), " (uint8 Typed Array [RFC8746])" }, + { QCborTag(65), " (uint16, big endian, Typed Array [RFC8746])" }, + { QCborTag(66), " (uint32, big endian, Typed Array [RFC8746])" }, + { QCborTag(67), " (uint64, big endian, Typed Array [RFC8746])" }, + { QCborTag(68), " (uint8 Typed Array, clamped arithmetic [RFC8746])" }, + { QCborTag(69), " (uint16, little endian, Typed Array [RFC8746])" }, + { QCborTag(70), " (uint32, little endian, Typed Array [RFC8746])" }, + { QCborTag(71), " (uint64, little endian, Typed Array [RFC8746])" }, + { QCborTag(72), " (sint8 Typed Array [RFC8746])" }, + { QCborTag(73), " (sint16, big endian, Typed Array [RFC8746])" }, + { QCborTag(74), " (sint32, big endian, Typed Array [RFC8746])" }, + { QCborTag(75), " (sint64, big endian, Typed Array [RFC8746])" }, + { QCborTag(76), " ((reserved) [RFC8746])" }, + { QCborTag(77), " (sint16, little endian, Typed Array [RFC8746])" }, + { QCborTag(78), " (sint32, little endian, Typed Array [RFC8746])" }, + { QCborTag(79), " (sint64, little endian, Typed Array [RFC8746])" }, + { QCborTag(80), " (IEEE 754 binary16, big endian, Typed Array [RFC8746])" }, + { QCborTag(81), " (IEEE 754 binary32, big endian, Typed Array [RFC8746])" }, + { QCborTag(82), " (IEEE 754 binary64, big endian, Typed Array [RFC8746])" }, + { QCborTag(83), " (IEEE 754 binary128, big endian, Typed Array [RFC8746])" }, + { QCborTag(84), " (IEEE 754 binary16, little endian, Typed Array [RFC8746])" }, + { QCborTag(85), " (IEEE 754 binary32, little endian, Typed Array [RFC8746])" }, + { QCborTag(86), " (IEEE 754 binary64, little endian, Typed Array [RFC8746])" }, + { QCborTag(87), " (IEEE 754 binary128, little endian, Typed Array [RFC8746])" }, + { QCborTag(96), " (COSE Encrypted Data Object [RFC9052])" }, + { QCborTag(97), " (COSE MACed Data Object [RFC9052])" }, + { QCborTag(98), " (COSE Signed Data Object [RFC9052])" }, + { QCborTag(100), " (Number of days since the epoch date 1970-01-01 [RFC8943])" }, + { QCborTag(101), " (alternatives as given by the uint + 128; see Section 9.1)" }, + { QCborTag(103), " (Geographic Coordinates)" }, + { QCborTag(104), " (Geographic Coordinate Reference System WKT or EPSG number)" }, + { QCborTag(110), " (relative object identifier (BER encoding); SDNV sequence [RFC9090])" }, + { QCborTag(111), " (object identifier (BER encoding) [RFC9090])" }, + { QCborTag(112), " (object identifier (BER encoding), relative to 1.3.6.1.4.1 [RFC9090])" }, + { QCborTag(120), " (Internet of Things Data Point)" }, + { QCborTag(260), + " (Network Address (IPv4 or IPv6 or MAC Address) (DEPRECATED in favor of 52 and 54 for IP" + " addresses) [RFC9164])" }, + { QCborTag(261), + " (Network Address Prefix (IPv4 or IPv6 Address + Mask Length) (DEPRECATED in favor of 52" + " and 54 for IP addresses) [RFC9164])" }, + { QCborTag(271), + " (DDoS Open Threat Signaling (DOTS) signal channel object, as defined in [RFC9132])" }, + { QCborTag(1004), " (full-date string [RFC8943])" }, + { QCborTag(1040), " (Multi-dimensional Array, column-major order [RFC8746])" }, + { QCborTag(55799), " (Self-described CBOR; see Section 3.4.6 [RFC8949])" }, + { QCborTag(55800), " (indicates that the file contains CBOR Sequences [RFC9277])" }, + { QCborTag(55801), + " (indicates that the file starts with a CBOR-Labeled Non-CBOR Data label. [RFC9277])" }, { QCborTag(-1), nullptr } }; // END GENERATED CODE enum { // See RFC 7049 section 2. - SmallValueBitLength = 5, - SmallValueMask = (1 << SmallValueBitLength) - 1, /* 0x1f */ - Value8Bit = 24, - Value16Bit = 25, - Value32Bit = 26, - Value64Bit = 27 + SmallValueBitLength = 5, + SmallValueMask = (1 << SmallValueBitLength) - 1, /* 0x1f */ + Value8Bit = 24, + Value16Bit = 25, + Value32Bit = 26, + Value64Bit = 27 }; +//! [0] struct CborDumper { - enum DumpOption { - ShowCompact = 0x01, - ShowWidthIndicators = 0x02, - ShowAnnotated = 0x04 - }; + enum DumpOption { ShowCompact = 0x01, ShowWidthIndicators = 0x02, ShowAnnotated = 0x04 }; Q_DECLARE_FLAGS(DumpOptions, DumpOption) CborDumper(QFile *f, DumpOptions opts_); @@ -160,6 +164,7 @@ private: qint64 offset = 0; DumpOptions opts; }; +//! [0] Q_DECLARE_OPERATORS_FOR_FLAGS(CborDumper::DumpOptions) static int cborNumberSize(quint64 value) @@ -176,8 +181,7 @@ static int cborNumberSize(quint64 value) return normalSize; } -CborDumper::CborDumper(QFile *f, DumpOptions opts_) - : opts(opts_) +CborDumper::CborDumper(QFile *f, DumpOptions opts_) : opts(opts_) { // try to mmap the file, this is faster char *ptr = reinterpret_cast<char *>(f->map(0, f->size(), QFile::MapPrivateOption)); @@ -224,51 +228,54 @@ QCborError CborDumper::dump() return err; } -template <typename T> static inline bool canConvertTo(double v) +template<typename T> +static inline bool canConvertTo(double v) { + using TypeInfo = std::numeric_limits<T>; // The [conv.fpint] (7.10 Floating-integral conversions) section of the // standard says only exact conversions are guaranteed. Converting // integrals to floating-point with loss of precision has implementation- // defined behavior whether the next higher or next lower is returned; // converting FP to integral is UB if it can't be represented.; - static_assert(std::numeric_limits<T>::is_integer); + static_assert(TypeInfo::is_integer); - double supremum = ldexp(1, std::numeric_limits<T>::digits); + double supremum = ldexp(1, TypeInfo::digits); if (v >= supremum) return false; - if (v < std::numeric_limits<T>::min()) // either zero or a power of two, so it's exact + if (v < TypeInfo::min()) // either zero or a power of two, so it's exact return false; // we're in range return v == floor(v); } -static QString fpToString(double v, const char *suffix) +static QString fpToString(double v, QLatin1StringView suffix = ""_L1) { if (qIsInf(v)) - return v < 0 ? QStringLiteral("-inf") : QStringLiteral("inf"); + return v < 0 ? "-inf"_L1 : "inf"_L1; if (qIsNaN(v)) - return QStringLiteral("nan"); + return "nan"_L1; if (canConvertTo<qint64>(v)) - return QString::number(qint64(v)) + ".0" + suffix; + return QString::number(qint64(v)) + ".0"_L1 + suffix; if (canConvertTo<quint64>(v)) - return QString::number(quint64(v)) + ".0" + suffix; + return QString::number(quint64(v)) + ".0"_L1 + suffix; QString s = QString::number(v, 'g', QLocale::FloatingPointShortest); - if (!s.contains('.') && !s.contains('e')) - s += '.'; - s += suffix; + if (!s.contains(u'.') && !s.contains(u'e')) + s += u'.'; + if (suffix.size()) + s += suffix; return s; }; void CborDumper::dumpOne(int nestingLevel) { - QString indent(1, QLatin1Char(' ')); + QString indent(1, u' '); QString indented = indent; if (!opts.testFlag(ShowCompact)) { - indent = QLatin1Char('\n') + QString(4 * nestingLevel, QLatin1Char(' ')); - indented = QLatin1Char('\n') + QString(4 + 4 * nestingLevel, QLatin1Char(' ')); + indent = u'\n' + QString(4 * nestingLevel, u' '); + indented = u'\n' + QString(4 + 4 * nestingLevel, u' '); } switch (reader.type()) { @@ -308,7 +315,7 @@ void CborDumper::dumpOne(int nestingLevel) printStringWidthIndicator(r.data.size()); r = reader.readByteArray(); - comma = QLatin1Char(',') + indented; + comma = u',' + indented; } } else { auto r = reader.readString(); @@ -317,7 +324,7 @@ void CborDumper::dumpOne(int nestingLevel) printStringWidthIndicator(r.data.toUtf8().size()); r = reader.readString(); - comma = QLatin1Char(',') + indented; + comma = u',' + indented; } } @@ -373,7 +380,7 @@ void CborDumper::dumpOne(int nestingLevel) if (reader.next()) { printWidthIndicator(quint64(tag)); printf("("); - dumpOne(nestingLevel); // same level! + dumpOne(nestingLevel); // same level! printf(")"); } @@ -405,15 +412,15 @@ void CborDumper::dumpOne(int nestingLevel) break; case QCborStreamReader::Float16: - printf("%s", qPrintable(fpToString(reader.toFloat16(), "f16"))); + printf("%s", qPrintable(fpToString(reader.toFloat16(), "f16"_L1))); reader.next(); break; case QCborStreamReader::Float: - printf("%s", qPrintable(fpToString(reader.toFloat(), "f"))); + printf("%s", qPrintable(fpToString(reader.toFloat(), "f"_L1))); reader.next(); break; case QCborStreamReader::Double: - printf("%s", qPrintable(fpToString(reader.toDouble(), ""))); + printf("%s", qPrintable(fpToString(reader.toDouble()))); reader.next(); break; case QCborStreamReader::Invalid: @@ -438,7 +445,7 @@ void CborDumper::dumpOneDetailed(int nestingLevel) if (cborNumberSize(value) != actualSize) printf(" (overlong)"); }; - auto print = [=](const char *descr, const char *fmt, ...) { + auto print = [&](const char *descr, const char *fmt, ...) { qint64 prevOffset = offset; offset = reader.currentOffset(); if (prevOffset == offset) @@ -466,13 +473,14 @@ void CborDumper::dumpOneDetailed(int nestingLevel) }; auto printFp = [=](const char *descr, double d) { - QString s = fpToString(d, ""); + QString s = fpToString(d); if (s.size() <= 6) return print(descr, "%s", qPrintable(s)); return print(descr, "%a", d); }; - auto printString = [=](const char *descr) { + auto printString = [&](const char *descr) { + constexpr qsizetype ChunkSizeLimit = std::numeric_limits<int>::max(); QByteArray indent(nestingLevel * 2, ' '); const char *chunkStr = (reader.isLengthKnown() ? "" : "chunk "); int width = 48 - indent.size(); @@ -480,8 +488,8 @@ void CborDumper::dumpOneDetailed(int nestingLevel) qsizetype size = reader.currentStringChunkSize(); if (size < 0) - return; // error - if (size >= std::numeric_limits<int>::max()) { + return; // error + if (size >= ChunkSizeLimit) { fprintf(stderr, "String length too big, %lli\n", qint64(size)); exit(EXIT_FAILURE); } @@ -525,7 +533,7 @@ void CborDumper::dumpOneDetailed(int nestingLevel) printf(" %s%s", indent.constData(), section.toHex(' ').constData()); // print the decode - QByteArray spaces(width > 0 ? width - section.size() * 3 + 1: 0, ' '); + QByteArray spaces(width > 0 ? width - section.size() * 3 + 1 : 0, ' '); printf("%s # \"", spaces.constData()); auto ptr = reinterpret_cast<const uchar *>(section.constData()); for (int j = 0; j < section.size(); ++j) @@ -537,8 +545,8 @@ void CborDumper::dumpOneDetailed(int nestingLevel) // get the next chunk size = reader.currentStringChunkSize(); if (size < 0) - return; // error - if (size >= std::numeric_limits<int>::max()) { + return; // error + if (size >= ChunkSizeLimit) { fprintf(stderr, "String length too big, %lli\n", qint64(size)); exit(EXIT_FAILURE); } @@ -676,7 +684,9 @@ void CborDumper::printByteArray(const QByteArray &ba) break; case quint8(QCborKnownTags::ExpectedBase64url): - printf("b64'%s'", ba.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals).constData()); + printf("b64'%s'", + ba.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals) + .constData()); break; } } @@ -717,23 +727,20 @@ int main(int argc, char *argv[]) setlocale(LC_ALL, "C"); QCommandLineParser parser; - parser.setApplicationDescription(QStringLiteral("CBOR Dumper tool")); + parser.setApplicationDescription("CBOR Dumper tool"_L1); parser.addHelpOption(); - QCommandLineOption compact({QStringLiteral("c"), QStringLiteral("compact")}, - QStringLiteral("Use compact form (no line breaks)")); + QCommandLineOption compact({"c"_L1, "compact"_L1}, "Use compact form (no line breaks)"_L1); parser.addOption(compact); - QCommandLineOption showIndicators({QStringLiteral("i"), QStringLiteral("indicators")}, - QStringLiteral("Show indicators for width of lengths and integrals")); + QCommandLineOption showIndicators({ "i"_L1, "indicators"_L1 }, + "Show indicators for width of lengths and integrals"_L1); parser.addOption(showIndicators); - QCommandLineOption verbose({QStringLiteral("a"), QStringLiteral("annotated")}, - QStringLiteral("Show bytes and annotated decoding")); + QCommandLineOption verbose({"a"_L1, "annotated"_L1}, "Show bytes and annotated decoding"_L1); parser.addOption(verbose); - parser.addPositionalArgument(QStringLiteral("[source]"), - QStringLiteral("CBOR file to read from")); + parser.addPositionalArgument("[source]"_L1, "CBOR file to read from"_L1); parser.process(app); @@ -748,7 +755,7 @@ int main(int argc, char *argv[]) QStringList files = parser.positionalArguments(); if (files.isEmpty()) files << "-"; - for (const QString &file : qAsConst(files)) { + for (const QString &file : std::as_const(files)) { QFile f(file); if (file == "-" ? f.open(stdin, QIODevice::ReadOnly) : f.open(QIODevice::ReadOnly)) { if (files.size() > 1) diff --git a/examples/corelib/serialization/cbordump/tag-transform.xslt b/examples/corelib/serialization/cbordump/tag-transform.xslt deleted file mode 100644 index 3cc1b9b293..0000000000 --- a/examples/corelib/serialization/cbordump/tag-transform.xslt +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0"?> -<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:a="http://www.iana.org/assignments" xmlns="http://www.iana.org/assignments" xmlns:_="http://www.iana.org/assignments" xmlns:DEFAULT="http://www.iana.org/assignments" version="1.0"> -<xsl:output omit-xml-declaration="yes" indent="no" method="text"/> -<xsl:template match="/a:registry[@id='cbor-tags']">struct CborTagDescription -{ - QCborTag tag; - const char *description; // with space and parentheses -}; - -// <xsl:value-of select="a:registry/a:title"/> -static const CborTagDescription tagDescriptions[] = { - // from https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml -<xsl:for-each select="a:registry/a:record"> - <xsl:sort select="a:value" data-type="number"/> - <xsl:if test="a:semantics != ''"> - <xsl:call-template name="row"/> - </xsl:if> - </xsl:for-each> { QCborTag(-1), nullptr } -}; -</xsl:template> -<xsl:template name="row"> { QCborTag(<xsl:value-of select="a:value"/>), " (<xsl:value-of select="a:semantics"/> <xsl:call-template name="xref"/>)" }, -</xsl:template> -<xsl:template name="xref"><xsl:if test="a:xref/@type = 'rfc'"> [<xsl:value-of select="translate(a:xref/@data,'rfc','RFC')"/>]</xsl:if> -</xsl:template> -</xsl:stylesheet> diff --git a/examples/corelib/serialization/convert/CMakeLists.txt b/examples/corelib/serialization/convert/CMakeLists.txt index 14de45381a..24ad5bbb6a 100644 --- a/examples/corelib/serialization/convert/CMakeLists.txt +++ b/examples/corelib/serialization/convert/CMakeLists.txt @@ -1,42 +1,43 @@ -# Generated from convert.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause cmake_minimum_required(VERSION 3.16) project(convert LANGUAGES CXX) -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) -set(CMAKE_AUTOUIC ON) - -if(NOT DEFINED INSTALL_EXAMPLESDIR) - set(INSTALL_EXAMPLESDIR "examples") +if (ANDROID) + message(FATAL_ERROR "This project cannot be built on Android.") endif() -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/serialization/convert") +find_package(Qt6 REQUIRED COMPONENTS Core) -find_package(Qt6 COMPONENTS Core) +qt_standard_project_setup() qt_add_executable(convert cborconverter.cpp cborconverter.h - converter.h + converter.cpp converter.h datastreamconverter.cpp datastreamconverter.h + debugtextdumper.cpp debugtextdumper.h jsonconverter.cpp jsonconverter.h main.cpp nullconverter.cpp nullconverter.h textconverter.cpp textconverter.h + variantorderedmap.h xmlconverter.cpp xmlconverter.h ) -set_target_properties(convert PROPERTIES - WIN32_EXECUTABLE FALSE - MACOSX_BUNDLE FALSE -) -target_link_libraries(convert PUBLIC - Qt::Core + +target_link_libraries(convert PRIVATE + Qt6::Core ) install(TARGETS convert - RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" - BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" - LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_app_script( + TARGET convert + OUTPUT_SCRIPT deploy_script + NO_UNSUPPORTED_PLATFORM_ERROR ) +install(SCRIPT ${deploy_script}) diff --git a/examples/corelib/serialization/convert/cborconverter.cpp b/examples/corelib/serialization/convert/cborconverter.cpp index 4fc8408983..969f2741e0 100644 --- a/examples/corelib/serialization/convert/cborconverter.cpp +++ b/examples/corelib/serialization/convert/cborconverter.cpp @@ -1,72 +1,29 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "cborconverter.h" +#include "variantorderedmap.h" +#include <QCborArray> +#include <QCborMap> #include <QCborStreamReader> #include <QCborStreamWriter> -#include <QCborMap> -#include <QCborArray> #include <QCborValue> #include <QDataStream> -#include <QFloat16> +#include <QDebug> #include <QFile> +#include <QFloat16> #include <QMetaType> #include <QTextStream> #include <stdio.h> +using namespace Qt::StringLiterals; + static CborConverter cborConverter; static CborDiagnosticDumper cborDiagnosticDumper; -static const char optionHelp[] = +static const char cborOptionHelp[] = "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" @@ -102,6 +59,7 @@ QT_END_NAMESPACE // 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. +//! [0] static QVariant convertCborValue(const QCborValue &value); static QVariant convertCborMap(const QCborMap &map) @@ -130,7 +88,9 @@ static QVariant convertCborValue(const QCborValue &value) return convertCborMap(value.toMap()); return value.toVariant(); } +//! [0] +//! [1] enum TrimFloatingPoint { Double, Float, Float16 }; static QCborValue convertFromVariant(const QVariant &v, TrimFloatingPoint fpTrimming) { @@ -161,42 +121,30 @@ static QCborValue convertFromVariant(const QVariant &v, TrimFloatingPoint fpTrim return QCborValue::fromVariant(v); } +//! [1] -QString CborDiagnosticDumper::name() +QString CborDiagnosticDumper::name() const { - return QStringLiteral("cbor-dump"); + return "cbor-dump"_L1; } -Converter::Direction CborDiagnosticDumper::directions() +Converter::Directions CborDiagnosticDumper::directions() const { - return Out; + return Direction::Out; } -Converter::Options CborDiagnosticDumper::outputOptions() +Converter::Options CborDiagnosticDumper::outputOptions() const { return SupportsArbitraryMapKeys; } -const char *CborDiagnosticDumper::optionsHelp() +const char *CborDiagnosticDumper::optionsHelp() const { 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) +void CborDiagnosticDumper::saveFile(QIODevice *f, const QVariant &contents, + const QStringList &options) const { QCborValue::DiagnosticNotationOptions opts = QCborValue::LineWrapped; for (const QString &s : options) { @@ -219,14 +167,12 @@ void CborDiagnosticDumper::saveFile(QIODevice *f, const QVariant &contents, cons } } - fprintf(stderr, "Unknown CBOR diagnostic option '%s'. Available options are:\n%s", - qPrintable(s), diagnosticHelp); - exit(EXIT_FAILURE); + qFatal("Unknown CBOR diagnostic option '%s'. Available options are:\n%s", + qPrintable(s), diagnosticHelp); } QTextStream out(f); - out << convertFromVariant(contents, Double).toDiagnosticNotation(opts) - << Qt::endl; + out << convertFromVariant(contents, Double).toDiagnosticNotation(opts) << Qt::endl; } CborConverter::CborConverter() @@ -234,36 +180,36 @@ CborConverter::CborConverter() qRegisterMetaType<QCborTag>(); } -QString CborConverter::name() +QString CborConverter::name() const { return "cbor"; } -Converter::Direction CborConverter::directions() +Converter::Directions CborConverter::directions() const { - return InOut; + return Direction::InOut; } -Converter::Options CborConverter::outputOptions() +Converter::Options CborConverter::outputOptions() const { return SupportsArbitraryMapKeys; } -const char *CborConverter::optionsHelp() +const char *CborConverter::optionsHelp() const { - return optionHelp; + return cborOptionHelp; } -bool CborConverter::probeFile(QIODevice *f) +bool CborConverter::probeFile(QIODevice *f) const { if (QFile *file = qobject_cast<QFile *>(f)) { - if (file->fileName().endsWith(QLatin1String(".cbor"))) + if (file->fileName().endsWith(".cbor"_L1)) return true; } return f->isReadable() && f->peek(3) == QByteArray("\xd9\xd9\xf7", 3); } -QVariant CborConverter::loadFile(QIODevice *f, Converter *&outputConverter) +QVariant CborConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const { const char *ptr = nullptr; if (auto file = qobject_cast<QFile *>(f)) @@ -280,25 +226,24 @@ QVariant CborConverter::loadFile(QIODevice *f, Converter *&outputConverter) 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); + qFatal().nospace() + << "Error loading CBOR contents (byte " << offset + << "): " << reader.lastError().toString() + << "\n bytes: " << (ptr ? mapped.mid(offset, 9) : f->read(9)); } else if (offset < mapped.size() || (!ptr && f->bytesAvailable())) { - fprintf(stderr, "Warning: bytes remaining at the end of the CBOR stream\n"); + qWarning("Warning: bytes remaining at the end of the CBOR stream"); } if (outputConverter == nullptr) outputConverter = &cborDiagnosticDumper; - else if (outputConverter == null) + else if (isNull(outputConverter)) 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) +void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) const { bool useSignature = true; bool useIntegers = true; @@ -354,13 +299,13 @@ void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStri } } - fprintf(stderr, "Unknown CBOR format option '%s'. Valid options are:\n%s", - qPrintable(s), optionHelp); - exit(EXIT_FAILURE); + qFatal("Unknown CBOR format option '%s'. Valid options are:\n%s", + qPrintable(s), cborOptionHelp); } - QCborValue v = convertFromVariant(contents, - useFloat16 == Always ? Float16 : useFloat == Always ? Float : Double); + QCborValue v = + convertFromVariant(contents, + useFloat16 == Always ? Float16 : useFloat == Always ? Float : Double); QCborStreamWriter writer(f); if (useSignature) writer.append(QCborKnownTags::Signature); @@ -374,4 +319,3 @@ void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStri opts |= QCborValue::UseFloat16; v.toCbor(writer, opts); } - diff --git a/examples/corelib/serialization/convert/cborconverter.h b/examples/corelib/serialization/convert/cborconverter.h index f0a89cb141..db68f99fda 100644 --- a/examples/corelib/serialization/convert/cborconverter.h +++ b/examples/corelib/serialization/convert/cborconverter.h @@ -1,52 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef CBORCONVERTER_H #define CBORCONVERTER_H @@ -57,13 +10,12 @@ 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; + QString name() const override; + Directions directions() const override; + Options outputOptions() const override; + const char *optionsHelp() const override; + void saveFile(QIODevice *f, const QVariant &contents, + const QStringList &options) const override; }; class CborConverter : public Converter @@ -73,13 +25,14 @@ public: // 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; + QString name() const override; + Directions directions() const override; + Options outputOptions() const override; + const char *optionsHelp() const override; + bool probeFile(QIODevice *f) const override; + QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override; + void saveFile(QIODevice *f, const QVariant &contents, + const QStringList &options) const override; }; #endif // CBORCONVERTER_H diff --git a/examples/corelib/serialization/convert/convert.pro b/examples/corelib/serialization/convert/convert.pro index 4c6b0b557a..7592de7a22 100644 --- a/examples/corelib/serialization/convert/convert.pro +++ b/examples/corelib/serialization/convert/convert.pro @@ -11,18 +11,22 @@ target.path = $$[QT_INSTALL_EXAMPLES]/corelib/serialization/convert INSTALLS += target SOURCES += main.cpp \ + converter.cpp \ cborconverter.cpp \ - jsonconverter.cpp \ datastreamconverter.cpp \ + debugtextdumper.cpp \ + jsonconverter.cpp \ + nullconverter.cpp \ textconverter.cpp \ - xmlconverter.cpp \ - nullconverter.cpp + xmlconverter.cpp HEADERS += \ converter.h \ cborconverter.h \ - jsonconverter.h \ datastreamconverter.h \ + debugtextdumper.h \ + jsonconverter.h \ + nullconverter.h \ textconverter.h \ - xmlconverter.h \ - nullconverter.h + variantorderedmap.h \ + xmlconverter.h diff --git a/examples/corelib/serialization/convert/converter.cpp b/examples/corelib/serialization/convert/converter.cpp new file mode 100644 index 0000000000..a57f305971 --- /dev/null +++ b/examples/corelib/serialization/convert/converter.cpp @@ -0,0 +1,44 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "converter.h" + +//! [0] +Converter::Converter() +{ + converters().append(this); +} + +Converter::~Converter() +{ + converters().removeAll(this); +} + +QList<const Converter *> &Converter::converters() +{ + Q_CONSTINIT static QList<const Converter *> store; + return store; +} + +const QList<const Converter *> &Converter::allConverters() +{ + return converters(); +} +//! [0] + +// Some virtual methods that Converter classes needn't override, when not relevant: +Converter::Options Converter::outputOptions() const { return {}; } +const char *Converter::optionsHelp() const { return nullptr; } +bool Converter::probeFile(QIODevice *) const { return false; } + +// The virtual method they should override if they claim to support In: +QVariant Converter::loadFile(QIODevice *, const Converter *&outputConverter) const +{ + Q_ASSERT(!directions().testFlag(Converter::Direction::In)); + // For those that don't, this should never be called. + Q_UNIMPLEMENTED(); + // But every implementation should at least do this: + if (!outputConverter) + outputConverter = this; + return QVariant(); +} diff --git a/examples/corelib/serialization/convert/converter.h b/examples/corelib/serialization/convert/converter.h index 56b501a076..40b7575a1e 100644 --- a/examples/corelib/serialization/convert/converter.h +++ b/examples/corelib/serialization/convert/converter.h @@ -1,103 +1,45 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef CONVERTER_H #define CONVERTER_H #include <QIODevice> -#include <QPair> -#include <QVariant> #include <QList> +#include <QStringList> +#include <QVariant> -class VariantOrderedMap : public QList<QPair<QVariant, QVariant>> -{ -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) - +//! [0] class Converter { + static QList<const Converter *> &converters(); protected: Converter(); + static bool isNull(const Converter *converter); // in nullconverter.cpp public: - static Converter *null; + static const QList<const Converter *> &allConverters(); - enum Direction { - In = 1, Out = 2, InOut = 3 - }; + enum class Direction { In = 1, Out = 2, InOut = In | Out }; + Q_DECLARE_FLAGS(Directions, Direction) - enum Option { - SupportsArbitraryMapKeys = 0x01 - }; + 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; + virtual QString name() const = 0; + virtual Directions directions() const = 0; + virtual Options outputOptions() const; + virtual const char *optionsHelp() const; + virtual bool probeFile(QIODevice *f) const; + virtual QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const; + virtual void saveFile(QIODevice *f, const QVariant &contents, + const QStringList &options) const = 0; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Directions) Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Options) +//! [0] #endif // CONVERTER_H diff --git a/examples/corelib/serialization/convert/datastreamconverter.cpp b/examples/corelib/serialization/convert/datastreamconverter.cpp index c459696b26..ce28fcb98e 100644 --- a/examples/corelib/serialization/convert/datastreamconverter.cpp +++ b/examples/corelib/serialization/convert/datastreamconverter.cpp @@ -1,68 +1,23 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "datastreamconverter.h" +#include "debugtextdumper.h" +#include "variantorderedmap.h" #include <QDataStream> -#include <QDebug> -#include <QTextStream> -static const char optionHelp[] = +using namespace Qt::StringLiterals; + +static const char dataStreamOptionHelp[] = "byteorder=host|big|little Byte order to use.\n" - "version=<n> QDataStream version (default: Qt 5.0).\n" + "version=<n> QDataStream version (default: Qt 6.0).\n" ; static const char signature[] = "qds"; -static DataStreamDumper dataStreamDumper; -static DataStreamConverter DataStreamConverter; +static DataStreamConverter dataStreamConverter; +static DebugTextDumper debugTextDumper; QDataStream &operator<<(QDataStream &ds, const VariantOrderedMap &map) { @@ -89,126 +44,44 @@ QDataStream &operator>>(QDataStream &ds, VariantOrderedMap &map) 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<VariantOrderedMap>() || type == QMetaType::QVariantMap) { - const auto map = (type == QMetaType::QVariantMap) ? - VariantOrderedMap(v.toMap()) : qvariant_cast<VariantOrderedMap>(v); - - 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 == QMetaType::QVariantList) { - 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<VariantOrderedMap>(); } -QString DataStreamConverter::name() +QString DataStreamConverter::name() const { - return QStringLiteral("datastream"); + return "datastream"_L1; } -Converter::Direction DataStreamConverter::directions() +Converter::Directions DataStreamConverter::directions() const { - return InOut; + return Direction::InOut; } -Converter::Options DataStreamConverter::outputOptions() +Converter::Options DataStreamConverter::outputOptions() const { return SupportsArbitraryMapKeys; } -const char *DataStreamConverter::optionsHelp() +const char *DataStreamConverter::optionsHelp() const { - return optionHelp; + return dataStreamOptionHelp; } -bool DataStreamConverter::probeFile(QIODevice *f) +bool DataStreamConverter::probeFile(QIODevice *f) const { return f->isReadable() && f->peek(sizeof(signature) - 1) == signature; } -QVariant DataStreamConverter::loadFile(QIODevice *f, Converter *&outputConverter) +QVariant DataStreamConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const { if (!outputConverter) - outputConverter = &dataStreamDumper; + outputConverter = &debugTextDumper; 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); - } + if (f->read(sizeof(signature) - 1) != signature || !f->getChar(&c) || (c != 'l' && c != 'B')) + qFatal("Could not load QDataStream file: invalid signature."); QDataStream ds(f); ds.setByteOrder(c == 'l' ? QDataStream::LittleEndian : QDataStream::BigEndian); @@ -222,9 +95,10 @@ QVariant DataStreamConverter::loadFile(QIODevice *f, Converter *&outputConverter return result; } -void DataStreamConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) +void DataStreamConverter::saveFile(QIODevice *f, const QVariant &contents, + const QStringList &options) const { - QDataStream::Version version = QDataStream::Qt_5_0; + QDataStream::Version version = QDataStream::Qt_6_0; auto order = QDataStream::ByteOrder(QSysInfo::ByteOrder); for (const QString &option : options) { const QStringList pair = option.split('='); @@ -249,18 +123,16 @@ void DataStreamConverter::saveFile(QIODevice *f, const QVariant &contents, const 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); + qFatal("Invalid version number '%s': must be a number from 1 to %d.", + qPrintable(pair.last()), QDataStream::Qt_DefaultCompiledVersion); } } - fprintf(stderr, "Unknown QDataStream formatting option '%s'. Available options are:\n%s", - qPrintable(option), optionHelp); - exit(EXIT_FAILURE); + qFatal("Unknown QDataStream formatting option '%s'. Available options are:\n%s", + qPrintable(option), dataStreamOptionHelp); } - char c = order == QDataStream::LittleEndian ? 'l' : 'B'; + char c = order == QDataStream::LittleEndian ? 'l' : 'B'; f->write(signature); f->write(&c, 1); diff --git a/examples/corelib/serialization/convert/datastreamconverter.h b/examples/corelib/serialization/convert/datastreamconverter.h index 1b74abc54f..201f3c4754 100644 --- a/examples/corelib/serialization/convert/datastreamconverter.h +++ b/examples/corelib/serialization/convert/datastreamconverter.h @@ -1,71 +1,11 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #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: @@ -73,13 +13,14 @@ public: // 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; + QString name() const override; + Directions directions() const override; + Options outputOptions() const override; + const char *optionsHelp() const override; + bool probeFile(QIODevice *f) const override; + QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override; + void saveFile(QIODevice *f, const QVariant &contents, + const QStringList &options) const override; }; #endif // DATASTREAMCONVERTER_H diff --git a/examples/corelib/serialization/convert/debugtextdumper.cpp b/examples/corelib/serialization/convert/debugtextdumper.cpp new file mode 100644 index 0000000000..f010bd8e2a --- /dev/null +++ b/examples/corelib/serialization/convert/debugtextdumper.cpp @@ -0,0 +1,74 @@ +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "debugtextdumper.h" +#include "variantorderedmap.h" + +#include <QDebug> +#include <QTextStream> + +using namespace Qt::StringLiterals; + +// Static instance is declared in datastreamconverter.cpp, since it uses it. + +static QString dumpVariant(const QVariant &v, const QString &indent = "\n"_L1) +{ + QString result; + QString indented = indent + " "_L1; + + int type = v.userType(); + if (type == qMetaTypeId<VariantOrderedMap>() || type == QMetaType::QVariantMap) { + const auto map = (type == QMetaType::QVariantMap) ? VariantOrderedMap(v.toMap()) + : qvariant_cast<VariantOrderedMap>(v); + + result = "Map {"_L1; + for (const auto &pair : map) { + result += indented + dumpVariant(pair.first, indented); + result.chop(1); // remove comma + result += " => "_L1 + dumpVariant(pair.second, indented); + } + result.chop(1); // remove comma + result += indent + "},"_L1; + } else if (type == QMetaType::QVariantList) { + const QVariantList list = v.toList(); + + result = "List ["_L1; + for (const auto &item : list) + result += indented + dumpVariant(item, indented); + result.chop(1); // remove comma + result += indent + "],"_L1; + } else { + QDebug debug(&result); + debug.nospace() << v << ','; + } + return result; +} + +QString DebugTextDumper::name() const +{ + return "debugtext-dump"_L1; +} + +Converter::Directions DebugTextDumper::directions() const +{ + return Direction::Out; +} + +Converter::Options DebugTextDumper::outputOptions() const +{ + return SupportsArbitraryMapKeys; +} + +void DebugTextDumper::saveFile(QIODevice *f, const QVariant &contents, + const QStringList &options) const +{ + if (!options.isEmpty()) { + qFatal("Unknown option '%s' to debug text output. This format has no options.", + qPrintable(options.first())); + } + QString s = dumpVariant(contents); + s[s.size() - 1] = u'\n'; // replace the comma with newline + + QTextStream out(f); + out << s; +} diff --git a/examples/corelib/serialization/convert/debugtextdumper.h b/examples/corelib/serialization/convert/debugtextdumper.h new file mode 100644 index 0000000000..7d3d762104 --- /dev/null +++ b/examples/corelib/serialization/convert/debugtextdumper.h @@ -0,0 +1,20 @@ +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef DEBUGTEXTDUMPER_H +#define DEBUGTEXTDUMPER_H + +#include "converter.h" + +class DebugTextDumper : public Converter +{ + // Converter interface +public: + QString name() const override; + Directions directions() const override; + Options outputOptions() const override; + void saveFile(QIODevice *f, const QVariant &contents, + const QStringList &options) const override; +}; + +#endif // DEBUGTEXTDUMPER_H diff --git a/examples/corelib/serialization/convert/doc/images/convert.png b/examples/corelib/serialization/convert/doc/images/convert.png Binary files differnew file mode 100644 index 0000000000..53e05b4333 --- /dev/null +++ b/examples/corelib/serialization/convert/doc/images/convert.png diff --git a/examples/corelib/serialization/convert/doc/src/convert.qdoc b/examples/corelib/serialization/convert/doc/src/convert.qdoc new file mode 100644 index 0000000000..187e81a85e --- /dev/null +++ b/examples/corelib/serialization/convert/doc/src/convert.qdoc @@ -0,0 +1,156 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \example serialization/convert + \examplecategory {Data Processing & I/O} + \meta tag {network} + \title Serialization Converter + + \brief How to convert between different serialization formats. + + This example converts between JSON, CBOR, XML, QDataStream and some simple + text formats. It can auto-detect the format being used, or be told which + format to use. Not all formats support both input and output, and they have + different sets of which content datatypes they support. QDataStream and XML + are the richest, followed by CBOR, then JSON, and then the plain text + formats. Conversion via the less capable formats is apt to lose structure + from the data. + + \image convert.png + + \sa {Parsing and displaying CBOR data}, {Saving and Loading a Game} + + \section1 The Converter Class + + The Converter class is the abstract superclass for all the converters to and + from all the formats. They all convert from or to the QVariant class, which + is used to represent all the datastructures internally. + + \snippet serialization/convert/converter.h 0 + + The Converter constructor and destructor manage a list of available + converters used by the main program so that it knows what converters are + available. Each converter type defines a static instance that ensures it is + constructed and thus available to the main program via this list. The \c + allConverters() method provides \c main()'s code with access to the list. + + \snippet serialization/convert/converter.cpp 0 + + The name() function returns the name of the converter. The directions() + function is used to determine if a converter can be used for input, output, + or both. These enable the main program to report what converters are + available in its help text for the command-line options to select input and + output formats. + + \snippet serialization/convert/main.cpp 0 + + The optionsHelp() function is used to report the various command-line + options supported by the available formats, when queried using its \c + {--format-options <format>} command-line option. + + \snippet serialization/convert/main.cpp 1 + + The outputOptions() function reports the output capabilities of a converter. + At present the only optional feature is support for arbitrary keys in + mappings from keys to values. An input converter's loadFile() can use this + information to tailor the form in which it presents the data it has read, to + be as faithfully represented by the output converter as its capabilities + permit. + + The probeFile() function is used to determine if a file matches the format + of the converter. The main program uses this to determine what format to use + when reading or writing a file, based on its name and potentially content, + when the user has not specified the format to use on the command-line. + + The loadFile() function deserializes data. The caller tells loadFile() which + serializer it intends to use, so that loadFile() can query its + outputOptions() to determine the form in which to represent the loaded data. + If the caller hasn't settled on a choice of output converter, loadFile() + supplies it with a default output converter suitable to the data it is + returning. + + The saveFile() function serializes data. It is passed options from the + command-line, as described by loadHelp(), that can tune the details of how + it represents the data when saving to file. + + Both loadFile() and saveFile() can be used with an arbitrary \l QIODevice. + This means that a Converter could also be used with a network socket or + other source of data, to read from or write to. In the present program, the + main program always passes a \l QFile, accessing either a file on disk or + one of the standard streams of the process. + + \section2 The Available Converters + + Several converters are supported, illustrating how the converter program + could be adapted to other formats, should the need arise. See the source + code for each for its details. The CBOR converters serve as a relatively + full-featured illustration of the ways converters can work, that we'll look + into in more detail below. This table summarizes the available converters: + + \table + \header \li Class \li mode \li format + \row \li CborConverter \li In/Out \li CBOR + \row \li CborDiagnosticDumper \li Out \li CBOR diagnostic + \row \li DataStreamConverter \li In/Out \li QDataStream + \row \li DebugTextDumper \li Out \li Lossless, non-standard, human-readable + \row \li JsonConverter \li In/Out \li JSON + \row \li NullConverter \li Out \li No output + \row \li TextConverter \li In/Out \li Structured plain text + \row \li XmlConverter \li In/Out \li XML + \endtable + + Those that support input use themselves as loadFile()'s fallback converter, + except for the CBOR and QDataStream converters, which use their respective + output-only dumper companion classes. The null converter can be used as + output converter when running the program for the sake of any validation or + verification that an input converter may perform. + + \section2 The CborConverter and CborDiagnosticDumper Classes + + The CborConverter class supports serializing to and from the CBOR format. + It supports various options to configure the output of floating point values + and a \c{signature} option to determine whether to start its output with a + CBOR tag that serves as a file header, identifying the file as containing + CBOR data. + + There is also a CborDiagnosticDumper class to output in CBOR diagnostic + notation. It does not support loading data. The form of its output can be + configured using two options. One selects whether to use the (more verbose) + extended CBOR diagnostic format. The other control whether each CBOR value + appears on a separate line. + + The plain diagnostic notation is similar to JSON, but not exactly, because + it supports displaying the contents of a CBOR stream losslessly, while a + conversion to JSON can be lossy. CborConverter's loadFile() uses + CborDiagnosticDumper for the fallback output converter, if its caller hasn't + determined the output format for itself. + + The convertCborValue(), convertCborMap() and convertCborArray() helper + functions are used to convert a QCborValue to a QVariant, for the benefit of + CborConverter::loadFile(). + + \snippet serialization/convert/cborconverter.cpp 0 + + The convertFromVariant() function is used to convert a QVariant to a + QCborValue for output by the \c saveFile() of either class. + + \snippet serialization/convert/cborconverter.cpp 1 + + \sa {CBOR Support in Qt} + + \section1 The convert program + + The \c main() function sets up a \l QApplication and a \l QCommandLineParser + to make sense of the options the user has specified and provide help if the + user asks for it. It uses the values obtained for the various \l + QCommandLineOption instances describing the user's choices, plus the + positional arguments for file names, to prepare the converters it will use. + + It then uses its input converter to load data (and possibly resolve its + choice of output converter, if it hasn't selected one yet) and its output + converter to serialize that data, taking account of any output options the + user has supplied on the command-line. + + \snippet serialization/convert/main.cpp 2 +*/ diff --git a/examples/corelib/serialization/convert/jsonconverter.cpp b/examples/corelib/serialization/convert/jsonconverter.cpp index ec24af703d..1b59ed5c1f 100644 --- a/examples/corelib/serialization/convert/jsonconverter.cpp +++ b/examples/corelib/serialization/convert/jsonconverter.cpp @@ -1,52 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "jsonconverter.h" @@ -56,49 +9,39 @@ #include <QJsonObject> #include <QJsonValue> +using namespace Qt::StringLiterals; + static JsonConverter jsonConverter; -static const char optionHelp[] = - "compact=no|yes Use compact JSON form.\n"; +static const char jsonOptionHelp[] = "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); - } + if (!doc.isObject() && !doc.isArray()) + qFatal("Could not convert contents to JSON."); return doc; } -JsonConverter::JsonConverter() -{ -} - -QString JsonConverter::name() -{ - return "json"; -} - -Converter::Direction JsonConverter::directions() +QString JsonConverter::name() const { - return InOut; + return "json"_L1; } -Converter::Options JsonConverter::outputOptions() +Converter::Directions JsonConverter::directions() const { - return {}; + return Direction::InOut; } -const char *JsonConverter::optionsHelp() +const char *JsonConverter::optionsHelp() const { - return optionHelp; + return jsonOptionHelp; } -bool JsonConverter::probeFile(QIODevice *f) +bool JsonConverter::probeFile(QIODevice *f) const { if (QFile *file = qobject_cast<QFile *>(f)) { - if (file->fileName().endsWith(QLatin1String(".json"))) + if (file->fileName().endsWith(".json"_L1)) return true; } @@ -109,7 +52,7 @@ bool JsonConverter::probeFile(QIODevice *f) return false; } -QVariant JsonConverter::loadFile(QIODevice *f, Converter *&outputConverter) +QVariant JsonConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const { if (!outputConverter) outputConverter = this; @@ -125,26 +68,26 @@ QVariant JsonConverter::loadFile(QIODevice *f, Converter *&outputConverter) 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); + qFatal("Could not parse JSON content: offset %d: %s", + error.offset, qPrintable(error.errorString())); } - if (outputConverter == null) + if (isNull(outputConverter)) return QVariant(); return doc.toVariant(); } -void JsonConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) +void JsonConverter::saveFile(QIODevice *f, const QVariant &contents, + const QStringList &options) const { QJsonDocument::JsonFormat format = QJsonDocument::Indented; for (const QString &s : options) { - if (s == QLatin1String("compact=no")) { + if (s == "compact=no"_L1) { format = QJsonDocument::Indented; - } else if (s == QLatin1String("compact=yes")) { + } else if (s == "compact=yes"_L1) { format = QJsonDocument::Compact; } else { - fprintf(stderr, "Unknown option '%s' to JSON output. Valid options are:\n%s", qPrintable(s), optionHelp); - exit(EXIT_FAILURE); + qFatal("Unknown option '%s' to JSON output. Valid options are:\n%s", + qPrintable(s), jsonOptionHelp); } } diff --git a/examples/corelib/serialization/convert/jsonconverter.h b/examples/corelib/serialization/convert/jsonconverter.h index 445a0e6695..e1dd1ecdb4 100644 --- a/examples/corelib/serialization/convert/jsonconverter.h +++ b/examples/corelib/serialization/convert/jsonconverter.h @@ -1,52 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef JSONCONVERTER_H #define JSONCONVERTER_H @@ -55,18 +8,15 @@ 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; + QString name() const override; + Directions directions() const override; + const char *optionsHelp() const override; + bool probeFile(QIODevice *f) const override; + QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override; + void saveFile(QIODevice *f, const QVariant &contents, + const QStringList &options) const override; }; #endif // JSONCONVERTER_H diff --git a/examples/corelib/serialization/convert/main.cpp b/examples/corelib/serialization/convert/main.cpp index 161fec6bcd..d3021fadca 100644 --- a/examples/corelib/serialization/convert/main.cpp +++ b/examples/corelib/serialization/convert/main.cpp @@ -1,52 +1,6 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 Intel Corporation. +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "converter.h" @@ -58,177 +12,144 @@ #include <stdio.h> -static QList<Converter *> *availableConverters; +using namespace Qt::StringLiterals; -Converter::Converter() +static const Converter *prepareConverter(QString format, Converter::Direction direction, + QFile *stream) { - if (!availableConverters) - availableConverters = new QList<Converter *>; - availableConverters->append(this); -} + const bool out = direction == Converter::Direction::Out; + const QIODevice::OpenMode mode = out + ? QIODevice::WriteOnly | QIODevice::Truncate + : QIODevice::ReadOnly; + const char *dirn = out ? "output" : "input"; + + if (stream->fileName().isEmpty()) + stream->open(out ? stdout : stdin, mode); + else + stream->open(mode); + + if (!stream->isOpen()) { + qFatal("Could not open \"%s\" for %s: %s", + qPrintable(stream->fileName()), dirn, qPrintable(stream->errorString())); + } else if (format == "auto"_L1) { + for (const Converter *conv : Converter::allConverters()) { + if (conv->directions().testFlag(direction) && conv->probeFile(stream)) + return conv; + } + if (out) // Failure to identify output format can be remedied by loadFile(). + return nullptr; -Converter::~Converter() -{ - availableConverters->removeAll(this); + // Input format, however, we must know before we can call that: + qFatal("Could not determine input format. Specify it with the -I option."); + } else { + for (const Converter *conv : Converter::allConverters()) { + if (conv->name() == format) { + if (!conv->directions().testFlag(direction)) { + qWarning("File format \"%s\" cannot be used for %s", + qPrintable(format), dirn); + continue; // on the off chance there's another with the same name + } + return conv; + } + } + qFatal("Unknown %s file format \"%s\"", dirn, qPrintable(format)); + } + Q_UNREACHABLE_RETURN(nullptr); } int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); +//! [0] QStringList inputFormats; QStringList outputFormats; - for (Converter *conv : qAsConst(*availableConverters)) { + for (const Converter *conv : Converter::allConverters()) { auto direction = conv->directions(); QString name = conv->name(); - if (direction & Converter::In) + if (direction.testFlag(Converter::Direction::In)) inputFormats << name; - if (direction & Converter::Out) + if (direction.testFlag(Converter::Direction::Out)) outputFormats << name; } +//! [0] inputFormats.sort(); outputFormats.sort(); - inputFormats.prepend("auto"); - outputFormats.prepend("auto"); + inputFormats.prepend("auto"_L1); + outputFormats.prepend("auto"_L1); QCommandLineParser parser; - parser.setApplicationDescription(QStringLiteral("Qt file format conversion tool")); + parser.setApplicationDescription("Qt serialization format conversion tool"_L1); 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"); + QCommandLineOption inputFormatOption(QStringList{ "I"_L1, "input-format"_L1 }); + inputFormatOption.setDescription( + "Select the input format for the input file. Available formats: "_L1 + + inputFormats.join(", "_L1)); + inputFormatOption.setValueName("format"_L1); 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"); + QCommandLineOption outputFormatOption(QStringList{ "O"_L1, "output-format"_L1 }); + outputFormatOption.setDescription( + "Select the output format for the output file. Available formats: "_L1 + + outputFormats.join(", "_L1)); + outputFormatOption.setValueName("format"_L1); 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..."); + QCommandLineOption optionOption(QStringList{ "o"_L1, "option"_L1 }); + optionOption.setDescription( + "Format-specific options. Use --format-options to find out what options are available."_L1); + optionOption.setValueName("options..."_L1); optionOption.setDefaultValues({}); parser.addOption(optionOption); - QCommandLineOption formatOptionsOption("format-options"); - formatOptionsOption.setDescription(QStringLiteral("Prints the list of valid options for --option for the converter format <format>.")); - formatOptionsOption.setValueName("format"); + QCommandLineOption formatOptionsOption("format-options"_L1); + formatOptionsOption.setDescription( + "Prints the list of valid options for --option for the converter format <format>."_L1); + formatOptionsOption.setValueName("format"_L1); 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.addPositionalArgument("[source]"_L1, "File to read from (stdin if none)"_L1); + parser.addPositionalArgument("[destination]"_L1, "File to write to (stdout if none)"_L1); parser.process(app); if (parser.isSet(formatOptionsOption)) { QString format = parser.value(formatOptionsOption); - for (Converter *conv : qAsConst(*availableConverters)) { +//! [1] + for (const Converter *conv : Converter::allConverters()) { 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)); + if (help) { + qInfo("The following options are available for format '%s':\n\n%s", + qPrintable(format), help); + } else { + qInfo("Format '%s' supports no options.", qPrintable(format)); + } return EXIT_SUCCESS; } } +//! [1] - 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; - } + qFatal("Unknown file format '%s'", qPrintable(format)); } +//! [2] QStringList files = parser.positionalArguments(); QFile input(files.value(0)); QFile output(files.value(1)); + const Converter *inconv = prepareConverter(parser.value(inputFormatOption), + Converter::Direction::In, &input); + const Converter *outconv = prepareConverter(parser.value(outputFormatOption), + Converter::Direction::Out, &output); - 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 + // Now finally perform the conversion: QVariant data = inconv->loadFile(&input, outconv); - Q_ASSERT_X(outconv, "Converter Tool", + Q_ASSERT_X(outconv, "Serialization Converter", "Internal error: converter format did not provide default"); outconv->saveFile(&output, data, parser.values(optionOption)); return EXIT_SUCCESS; +//! [2] } diff --git a/examples/corelib/serialization/convert/nullconverter.cpp b/examples/corelib/serialization/convert/nullconverter.cpp index 2de492e64e..fb8be5c944 100644 --- a/examples/corelib/serialization/convert/nullconverter.cpp +++ b/examples/corelib/serialization/convert/nullconverter.cpp @@ -1,97 +1,37 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "nullconverter.h" -static NullConverter nullConverter; -Converter* Converter::null = &nullConverter; - -QString NullConverter::name() -{ - return QLatin1String("null"); -} - -Converter::Direction NullConverter::directions() -{ - return Out; -} +using namespace Qt::StringLiterals; -Converter::Options NullConverter::outputOptions() +static NullConverter nullConverter; +bool Converter::isNull(const Converter *converter) { - return SupportsArbitraryMapKeys; + return converter == &nullConverter; } -const char *NullConverter::optionsHelp() +QString NullConverter::name() const { - return nullptr; + return "null"_L1; } -bool NullConverter::probeFile(QIODevice *f) +Converter::Directions NullConverter::directions() const { - Q_UNUSED(f); - return false; + return Direction::Out; } -QVariant NullConverter::loadFile(QIODevice *f, Converter *&outputConverter) +Converter::Options NullConverter::outputOptions() const { - Q_UNUSED(f); - Q_UNUSED(outputConverter); - outputConverter = this; - return QVariant(); + return SupportsArbitraryMapKeys; } -void NullConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) +void NullConverter::saveFile(QIODevice *f, const QVariant &contents, + const QStringList &options) const { if (!options.isEmpty()) { - fprintf(stderr, "Unknown option '%s' to null output. This format has no options.\n", qPrintable(options.first())); - exit(EXIT_FAILURE); + qFatal("Unknown option '%s' to null output. This format has no options.", + qPrintable(options.first())); } Q_UNUSED(f); diff --git a/examples/corelib/serialization/convert/nullconverter.h b/examples/corelib/serialization/convert/nullconverter.h index af7339f092..1bdf9f22f7 100644 --- a/examples/corelib/serialization/convert/nullconverter.h +++ b/examples/corelib/serialization/convert/nullconverter.h @@ -1,52 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef NULLCONVERTER_H #define NULLCONVERTER_H @@ -57,13 +10,11 @@ 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; + QString name() const override; + Directions directions() const override; + Options outputOptions() const override; + void saveFile(QIODevice *f, const QVariant &contents, + const QStringList &options) const override; }; #endif // NULLCONVERTER_H diff --git a/examples/corelib/serialization/convert/textconverter.cpp b/examples/corelib/serialization/convert/textconverter.cpp index ae03b9a334..1f8b4f9c19 100644 --- a/examples/corelib/serialization/convert/textconverter.cpp +++ b/examples/corelib/serialization/convert/textconverter.cpp @@ -1,58 +1,13 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "textconverter.h" #include <QFile> #include <QTextStream> +using namespace Qt::StringLiterals; + static void dumpVariant(QTextStream &out, const QVariant &v) { switch (v.userType()) { @@ -89,68 +44,52 @@ static void dumpVariant(QTextStream &out, const QVariant &v) } } -QString TextConverter::name() -{ - return QStringLiteral("text"); -} - -Converter::Direction TextConverter::directions() -{ - return InOut; -} - -Converter::Options TextConverter::outputOptions() +QString TextConverter::name() const { - return {}; + return "text"_L1; } -const char *TextConverter::optionsHelp() +Converter::Directions TextConverter::directions() const { - return nullptr; + return Direction::InOut; } -bool TextConverter::probeFile(QIODevice *f) +bool TextConverter::probeFile(QIODevice *f) const { if (QFile *file = qobject_cast<QFile *>(f)) - return file->fileName().endsWith(QLatin1String(".txt")); + return file->fileName().endsWith(".txt"_L1); return false; } -QVariant TextConverter::loadFile(QIODevice *f, Converter *&outputConverter) +QVariant TextConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const { if (!outputConverter) outputConverter = this; QVariantList list; QTextStream in(f); - QString line ; + 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) { + if (qint64 v = line.toLongLong(&ok); ok) + list.append(v); + else if (double d = line.toDouble(&ok); ok) list.append(d); - continue; - } - - list.append(line); + else + list.append(line); } return list; } -void TextConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) +void TextConverter::saveFile(QIODevice *f, const QVariant &contents, + const QStringList &options) const { if (!options.isEmpty()) { - fprintf(stderr, "Unknown option '%s' to text output. This format has no options.\n", qPrintable(options.first())); - exit(EXIT_FAILURE); + qFatal("Unknown option '%s' to text output. This format has no options.", + qPrintable(options.first())); } QTextStream out(f); diff --git a/examples/corelib/serialization/convert/textconverter.h b/examples/corelib/serialization/convert/textconverter.h index 66f5136c02..526f295517 100644 --- a/examples/corelib/serialization/convert/textconverter.h +++ b/examples/corelib/serialization/convert/textconverter.h @@ -1,52 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef TEXTCONVERTER_H #define TEXTCONVERTER_H @@ -55,16 +8,14 @@ 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; + QString name() const override; + Directions directions() const override; + bool probeFile(QIODevice *f) const override; + QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override; + void saveFile(QIODevice *f, const QVariant &contents, + const QStringList &options) const override; }; #endif // TEXTCONVERTER_H diff --git a/examples/corelib/serialization/convert/variantorderedmap.h b/examples/corelib/serialization/convert/variantorderedmap.h new file mode 100644 index 0000000000..c65316b182 --- /dev/null +++ b/examples/corelib/serialization/convert/variantorderedmap.h @@ -0,0 +1,24 @@ +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef VARIANTORDEREDMAP_H +#define VARIANTORDEREDMAP_H + +#include <QList> +#include <QPair> +#include <QVariant> +#include <QVariantMap> + +class VariantOrderedMap : public QList<QPair<QVariant, QVariant>> +{ +public: + VariantOrderedMap() = default; + VariantOrderedMap(const QVariantMap &map) + { + reserve(map.size()); + for (auto it = map.begin(); it != map.end(); ++it) + append({it.key(), it.value()}); + } +}; + +#endif // VARIANTORDEREDMAP_H diff --git a/examples/corelib/serialization/convert/xmlconverter.cpp b/examples/corelib/serialization/convert/xmlconverter.cpp index c2811c5745..ef71fecb9f 100644 --- a/examples/corelib/serialization/convert/xmlconverter.cpp +++ b/examples/corelib/serialization/convert/xmlconverter.cpp @@ -1,54 +1,8 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "xmlconverter.h" +#include "variantorderedmap.h" #include <QBitArray> #include <QtCborCommon> @@ -60,8 +14,9 @@ #include <QXmlStreamReader> #include <QXmlStreamWriter> -static const char optionHelp[] = - "compact=no|yes Use compact XML form.\n"; +using namespace Qt::StringLiterals; + +static const char xmlOptionHelp[] = "compact=no|yes Use compact XML form.\n"; static XmlConverter xmlConverter; @@ -70,7 +25,7 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options static QVariantList listFromXml(QXmlStreamReader &xml, Converter::Options options) { QVariantList list; - while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == QLatin1String("list"))) { + while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "list"_L1)) { xml.readNext(); switch (xml.tokenType()) { case QXmlStreamReader::StartElement: @@ -94,20 +49,19 @@ static QVariantList listFromXml(QXmlStreamReader &xml, Converter::Options option break; } - fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", - xml.lineNumber(), xml.columnNumber(), - qPrintable(xml.tokenString()), qPrintable(xml.name().toString())); - exit(EXIT_FAILURE); + qFatal("%lld:%lld: Invalid XML %s '%s'.", xml.lineNumber(), xml.columnNumber(), + qPrintable(xml.tokenString()), qPrintable(xml.name().toString())); } xml.readNext(); return list; } -static VariantOrderedMap::value_type mapEntryFromXml(QXmlStreamReader &xml, Converter::Options options) +static VariantOrderedMap::value_type mapEntryFromXml(QXmlStreamReader &xml, + Converter::Options options) { QVariant key, value; - while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == QLatin1String("entry"))) { + while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "entry"_L1)) { xml.readNext(); switch (xml.tokenType()) { case QXmlStreamReader::StartElement: @@ -136,10 +90,8 @@ static VariantOrderedMap::value_type mapEntryFromXml(QXmlStreamReader &xml, Conv break; } - fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", - xml.lineNumber(), xml.columnNumber(), - qPrintable(xml.tokenString()), qPrintable(xml.name().toString())); - exit(EXIT_FAILURE); + qFatal("%lld:%lld: Invalid XML %s '%s'.", xml.lineNumber(), xml.columnNumber(), + qPrintable(xml.tokenString()), qPrintable(xml.name().toString())); } return { key, value }; @@ -150,11 +102,11 @@ static QVariant mapFromXml(QXmlStreamReader &xml, Converter::Options options) QVariantMap map1; VariantOrderedMap map2; - while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == QLatin1String("map"))) { + while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "map"_L1)) { xml.readNext(); switch (xml.tokenType()) { case QXmlStreamReader::StartElement: - if (xml.name() == QLatin1String("entry")) { + if (xml.name() == "entry"_L1) { auto pair = mapEntryFromXml(xml, options); if (options & Converter::SupportsArbitraryMapKeys) map2.append(pair); @@ -181,10 +133,8 @@ static QVariant mapFromXml(QXmlStreamReader &xml, Converter::Options options) break; } - fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", - xml.lineNumber(), xml.columnNumber(), - qPrintable(xml.tokenString()), qPrintable(xml.name().toString())); - exit(EXIT_FAILURE); + qFatal("%lld:%lld: Invalid XML %s '%s'.", xml.lineNumber(), xml.columnNumber(), + qPrintable(xml.tokenString()), qPrintable(xml.name().toString())); } xml.readNext(); @@ -196,18 +146,17 @@ static QVariant mapFromXml(QXmlStreamReader &xml, Converter::Options options) static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options) { QStringView name = xml.name(); - if (name == QLatin1String("list")) + if (name == "list"_L1) return listFromXml(xml, options); - if (name == QLatin1String("map")) + if (name == "map"_L1) 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); + if (name != "value"_L1) { + qFatal("%lld:%lld: Invalid XML key '%s'.", + xml.lineNumber(), xml.columnNumber(), qPrintable(name.toString())); } QXmlStreamAttributes attrs = xml.attributes(); - QStringView type = attrs.value(QLatin1String("type")); + QStringView type = attrs.value("type"_L1); forever { xml.readNext(); @@ -216,10 +165,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options 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); + qFatal("%lld:%lld: Invalid XML %s '%s'.", xml.lineNumber(), xml.columnNumber(), + qPrintable(xml.tokenString()), qPrintable(name.toString())); } QStringView text = xml.text(); @@ -227,45 +174,43 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options text = text.trimmed(); QVariant result; - bool ok; if (type.isEmpty()) { // ok - } else if (type == QLatin1String("number")) { + } else if (type == "number"_L1) { // try integer first + bool ok; 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); + qFatal("%lld:%lld: Invalid XML: could not interpret '%s' as a number.", + xml.lineNumber(), xml.columnNumber(), qPrintable(text.toString())); } + result = d; } - } else if (type == QLatin1String("bytes")) { + } else if (type == "bytes"_L1) { QByteArray data = text.toLatin1(); QStringView encoding = attrs.value("encoding"); - if (encoding == QLatin1String("base64url")) { + if (encoding == "base64url"_L1) { result = QByteArray::fromBase64(data, QByteArray::Base64UrlEncoding); - } else if (encoding == QLatin1String("hex")) { + } else if (encoding == "hex"_L1) { result = QByteArray::fromHex(data); - } else if (encoding.isEmpty() || encoding == QLatin1String("base64")) { + } else if (encoding.isEmpty() || encoding == "base64"_L1) { 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); + qFatal("%lld:%lld: Invalid XML: unknown encoding '%s' for bytes.", + xml.lineNumber(), xml.columnNumber(), qPrintable(encoding.toString())); } - } else if (type == QLatin1String("string")) { + } else if (type == "string"_L1) { result = text.toString(); - } else if (type == QLatin1String("null")) { + } else if (type == "null"_L1) { result = QVariant::fromValue(nullptr); - } else if (type == QLatin1String("CBOR simple type")) { + } else if (type == "CBOR simple type"_L1) { result = QVariant::fromValue(QCborSimpleType(text.toShort())); - } else if (type == QLatin1String("bits")) { + } else if (type == "bits"_L1) { QBitArray ba; ba.resize(text.size()); qsizetype n = 0; @@ -276,36 +221,33 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options } 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); + qFatal("%lld:%lld: Invalid XML: invalid bit string '%s'.", + xml.lineNumber(), xml.columnNumber(), qPrintable(text.toString())); } } ba.resize(n); result = ba; } else { int id = QMetaType::UnknownType; - if (type == QLatin1String("datetime")) + if (type == "datetime"_L1) id = QMetaType::QDateTime; - else if (type == QLatin1String("url")) + else if (type == "url"_L1) id = QMetaType::QUrl; - else if (type == QLatin1String("uuid")) + else if (type == "uuid"_L1) id = QMetaType::QUuid; - else if (type == QLatin1String("regex")) + else if (type == "regex"_L1) id = QMetaType::QRegularExpression; else id = QMetaType::fromName(type.toLatin1()).id(); if (id == QMetaType::UnknownType) { - fprintf(stderr, "%lld:%lld: Invalid XML: unknown type '%s'.\n", - xml.lineNumber(), xml.columnNumber(), qPrintable(type.toString())); - exit(EXIT_FAILURE); + qFatal("%lld:%lld: Invalid XML: unknown type '%s'.", + xml.lineNumber(), xml.columnNumber(), qPrintable(type.toString())); } result = text.toString(); if (!result.convert(QMetaType(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); + qFatal("%lld:%lld: Invalid XML: could not parse content as type '%s'.", + xml.lineNumber(), xml.columnNumber(), qPrintable(type.toString())); } } @@ -314,10 +256,8 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options } 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); + qFatal("%lld:%lld: Invalid XML %s '%s'.", xml.lineNumber(), xml.columnNumber(), + qPrintable(xml.tokenString()), qPrintable(name.toString())); } xml.readNext(); @@ -334,9 +274,9 @@ static void variantToXml(QXmlStreamWriter &xml, const QVariant &v) variantToXml(xml, v); xml.writeEndElement(); } else if (type == QMetaType::QVariantMap || type == qMetaTypeId<VariantOrderedMap>()) { - const VariantOrderedMap map = (type == QMetaType::QVariantMap) ? - VariantOrderedMap(v.toMap()) : - qvariant_cast<VariantOrderedMap>(v); + const VariantOrderedMap map = (type == QMetaType::QVariantMap) + ? VariantOrderedMap(v.toMap()) + : qvariant_cast<VariantOrderedMap>(v); xml.writeStartElement("map"); for (const auto &pair : map) { @@ -348,7 +288,7 @@ static void variantToXml(QXmlStreamWriter &xml, const QVariant &v) xml.writeEndElement(); } else { xml.writeStartElement("value"); - QString typeString = QStringLiteral("type"); + QString typeString = "type"_L1; switch (type) { case QMetaType::Short: case QMetaType::UShort: @@ -437,8 +377,7 @@ static void variantToXml(QXmlStreamWriter &xml, const QVariant &v) 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); + qFatal("XML: don't know how to serialize type '%s'.", typeName); } } } @@ -446,37 +385,37 @@ static void variantToXml(QXmlStreamWriter &xml, const QVariant &v) } } -QString XmlConverter::name() +QString XmlConverter::name() const { - return QStringLiteral("xml"); + return "xml"_L1; } -Converter::Direction XmlConverter::directions() +Converter::Directions XmlConverter::directions() const { - return InOut; + return Direction::InOut; } -Converter::Options XmlConverter::outputOptions() +Converter::Options XmlConverter::outputOptions() const { return SupportsArbitraryMapKeys; } -const char *XmlConverter::optionsHelp() +const char *XmlConverter::optionsHelp() const { - return optionHelp; + return xmlOptionHelp; } -bool XmlConverter::probeFile(QIODevice *f) +bool XmlConverter::probeFile(QIODevice *f) const { if (QFile *file = qobject_cast<QFile *>(f)) { - if (file->fileName().endsWith(QLatin1String(".xml"))) + if (file->fileName().endsWith(".xml"_L1)) return true; } return f->isReadable() && f->peek(5) == "<?xml"; } -QVariant XmlConverter::loadFile(QIODevice *f, Converter *&outputConverter) +QVariant XmlConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const { if (!outputConverter) outputConverter = this; @@ -484,25 +423,24 @@ QVariant XmlConverter::loadFile(QIODevice *f, Converter *&outputConverter) QXmlStreamReader xml(f); xml.readNextStartElement(); QVariant v = variantFromXml(xml, outputConverter->outputOptions()); - if (xml.hasError()) { - fprintf(stderr, "XML error: %s", qPrintable(xml.errorString())); - exit(EXIT_FAILURE); - } + if (xml.hasError()) + qFatal("XML error: %s", qPrintable(xml.errorString())); return v; } -void XmlConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) +void XmlConverter::saveFile(QIODevice *f, const QVariant &contents, + const QStringList &options) const { bool compact = false; for (const QString &s : options) { - if (s == QLatin1String("compact=no")) { + if (s == "compact=no"_L1) { compact = false; - } else if (s == QLatin1String("compact=yes")) { + } else if (s == "compact=yes"_L1) { compact = true; } else { - fprintf(stderr, "Unknown option '%s' to XML output. Valid options are:\n%s", qPrintable(s), optionHelp); - exit(EXIT_FAILURE); + qFatal("Unknown option '%s' to XML output. Valid options are:\n%s", + qPrintable(s), xmlOptionHelp); } } diff --git a/examples/corelib/serialization/convert/xmlconverter.h b/examples/corelib/serialization/convert/xmlconverter.h index 8fc0fea592..4888b0616f 100644 --- a/examples/corelib/serialization/convert/xmlconverter.h +++ b/examples/corelib/serialization/convert/xmlconverter.h @@ -1,52 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef XMLCONVERTER_H #define XMLCONVERTER_H @@ -57,13 +10,14 @@ 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; + QString name() const override; + Directions directions() const override; + Options outputOptions() const override; + const char *optionsHelp() const override; + bool probeFile(QIODevice *f) const override; + QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override; + void saveFile(QIODevice *f, const QVariant &contents, + const QStringList &options) const override; }; #endif // XMLCONVERTER_H diff --git a/examples/corelib/serialization/savegame/CMakeLists.txt b/examples/corelib/serialization/savegame/CMakeLists.txt index 18eda05e5d..14621ccc23 100644 --- a/examples/corelib/serialization/savegame/CMakeLists.txt +++ b/examples/corelib/serialization/savegame/CMakeLists.txt @@ -1,21 +1,16 @@ -# Generated from savegame.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause cmake_minimum_required(VERSION 3.16) project(savegame LANGUAGES CXX) -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) -set(CMAKE_AUTOUIC ON) - -if(NOT DEFINED INSTALL_EXAMPLESDIR) - set(INSTALL_EXAMPLESDIR "examples") +if (ANDROID) + message(FATAL_ERROR "This project cannot be built on Android.") endif() -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/serialization/savegame") +find_package(Qt6 REQUIRED COMPONENTS Core) -find_package(Qt6 COMPONENTS Core) +qt_standard_project_setup() qt_add_executable(savegame character.cpp character.h @@ -23,16 +18,20 @@ qt_add_executable(savegame level.cpp level.h main.cpp ) -set_target_properties(savegame PROPERTIES - WIN32_EXECUTABLE FALSE - MACOSX_BUNDLE FALSE -) -target_link_libraries(savegame PUBLIC - Qt::Core + +target_link_libraries(savegame PRIVATE + Qt6::Core ) install(TARGETS savegame - RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" - BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" - LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_app_script( + TARGET savegame + OUTPUT_SCRIPT deploy_script + NO_UNSUPPORTED_PLATFORM_ERROR ) +install(SCRIPT ${deploy_script}) diff --git a/examples/corelib/serialization/savegame/character.cpp b/examples/corelib/serialization/savegame/character.cpp index 046cde3091..863fcb9153 100644 --- a/examples/corelib/serialization/savegame/character.cpp +++ b/examples/corelib/serialization/savegame/character.cpp @@ -1,69 +1,15 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "character.h" #include <QMetaEnum> #include <QTextStream> -Character::Character() : - mLevel(0), - mClassType(Warrior) { -} +Character::Character() = default; -Character::Character(const QString &name, - int level, - Character::ClassType classType) : - mName(name), - mLevel(level), - mClassType(classType) +Character::Character(const QString &name, int level, Character::ClassType classType) + : mName(name), mLevel(level), mClassType(classType) { } @@ -97,35 +43,41 @@ void Character::setClassType(Character::ClassType classType) mClassType = classType; } -//! [0] -void Character::read(const QJsonObject &json) +//! [fromJson] +Character Character::fromJson(const QJsonObject &json) { - if (json.contains("name") && json["name"].isString()) - mName = json["name"].toString(); + Character result; + + if (const QJsonValue v = json["name"]; v.isString()) + result.mName = v.toString(); + + if (const QJsonValue v = json["level"]; v.isDouble()) + result.mLevel = v.toInt(); - if (json.contains("level") && json["level"].isDouble()) - mLevel = json["level"].toInt(); + if (const QJsonValue v = json["classType"]; v.isDouble()) + result.mClassType = ClassType(v.toInt()); - if (json.contains("classType") && json["classType"].isDouble()) - mClassType = ClassType(json["classType"].toInt()); + return result; } -//! [0] +//! [fromJson] -//! [1] -void Character::write(QJsonObject &json) const +//! [toJson] +QJsonObject Character::toJson() const { + QJsonObject json; json["name"] = mName; json["level"] = mLevel; json["classType"] = mClassType; + return json; } -//! [1] +//! [toJson] -void Character::print(int indentation) const +void Character::print(QTextStream &s, int indentation) const { const QString indent(indentation * 2, ' '); - QTextStream(stdout) << indent << "Name:\t" << mName << "\n"; - QTextStream(stdout) << indent << "Level:\t" << mLevel << "\n"; + const QString className = QMetaEnum::fromType<ClassType>().valueToKey(mClassType); - QString className = QMetaEnum::fromType<ClassType>().valueToKey(mClassType); - QTextStream(stdout) << indent << "Class:\t" << className << "\n"; + s << indent << "Name:\t" << mName << "\n" + << indent << "Level:\t" << mLevel << "\n" + << indent << "Class:\t" << className << "\n"; } diff --git a/examples/corelib/serialization/savegame/character.h b/examples/corelib/serialization/savegame/character.h index cbf06d7fd6..0504750320 100644 --- a/examples/corelib/serialization/savegame/character.h +++ b/examples/corelib/serialization/savegame/character.h @@ -1,52 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef CHARACTER_H #define CHARACTER_H @@ -55,15 +8,15 @@ #include <QObject> #include <QString> +QT_FORWARD_DECLARE_CLASS(QTextStream) + //! [0] class Character { - Q_GADGET; + Q_GADGET public: - enum ClassType { - Warrior, Mage, Archer - }; + enum ClassType { Warrior, Mage, Archer }; Q_ENUM(ClassType) Character(); @@ -78,14 +31,15 @@ public: ClassType classType() const; void setClassType(ClassType classType); - void read(const QJsonObject &json); - void write(QJsonObject &json) const; + static Character fromJson(const QJsonObject &json); + QJsonObject toJson() const; + + void print(QTextStream &s, int indentation = 0) const; - void print(int indentation = 0) const; private: QString mName; - int mLevel; - ClassType mClassType; + int mLevel = 0; + ClassType mClassType = Warrior; }; //! [0] diff --git a/examples/corelib/serialization/savegame/doc/src/savegame.qdoc b/examples/corelib/serialization/savegame/doc/src/savegame.qdoc index 72dd393cb0..46fca15b62 100644 --- a/examples/corelib/serialization/savegame/doc/src/savegame.qdoc +++ b/examples/corelib/serialization/savegame/doc/src/savegame.qdoc @@ -1,44 +1,21 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** 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. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: https://www.gnu.org/licenses/fdl-1.3.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! \example serialization/savegame - \title JSON Save Game Example + \examplecategory {Data Processing & I/O} + \title Saving and Loading a Game - \brief The JSON Save Game example demonstrates how to save and load a - small game using QJsonDocument, QJsonObject and QJsonArray. + \brief How to save and load a game using Qt's JSON or CBOR classes. Many games provide save functionality, so that the player's progress through the game can be saved and loaded at a later time. The process of saving a - game generally involves serializing each game object's member variables - to a file. Many formats can be used for this purpose, one of which is JSON. - With QJsonDocument, you also have the ability to serialize a document in a - \l {RFC 7049} {CBOR} format, which is great if you - don't want the save file to be readable, or if you need to keep the file size down. + game generally involves serializing each game object's member variables to a + file. Many formats can be used for this purpose, one of which is JSON. With + QJsonDocument, you also have the ability to serialize a document in a \l + {RFC 7049} {CBOR} format, which is great if you don't want the save file to + be easy to read (but see \l {Parsing and displaying CBOR data} for how it \e + can be read), or if you need to keep the file size down. In this example, we'll demonstrate how to save and load a simple game to and from JSON and binary formats. @@ -48,45 +25,83 @@ The Character class represents a non-player character (NPC) in our game, and stores the player's name, level, and class type. - It provides read() and write() functions to serialise its member variables. + It provides static fromJson() and non-static toJson() functions to + serialise itself. + + \note This pattern (fromJson()/toJson()) works because QJsonObjects can be + constructed independent of an owning QJsonDocument, and because the data + types being (de)serialized here are value types, so can be copied. When + serializing to another format — for example XML or QDataStream, which require passing + a document-like object — or when the object identity is important (QObject + subclasses, for example), other patterns may be more suitable. See the + \l{dombookmarks} example for XML, and the implementation of + \l QListWidgetItem::read() and \l QListWidgetItem::write() + for idiomatic QDataStream serialization. The \c{print()} functions in this example + are good examples of QTextStream serialization, even though they, of course, lack + the deserialization side. \snippet serialization/savegame/character.h 0 - Of particular interest to us are the read and write function + Of particular interest to us are the fromJson() and toJson() function implementations: - \snippet serialization/savegame/character.cpp 0 + \snippet serialization/savegame/character.cpp fromJson - In the read() function, we assign Character's members values from the - QJsonObject argument. You can use either \l QJsonObject::operator[]() or - QJsonObject::value() to access values within the JSON object; both are - const functions and return QJsonValue::Undefined if the key is invalid. We - check if the keys are valid before attempting to read them with - QJsonObject::contains(). + In the fromJson() function, we construct a local \c result Character object + and assign \c{result}'s members values from the QJsonObject argument. You + can use either \l QJsonObject::operator[]() or QJsonObject::value() to + access values within the JSON object; both are const functions and return + QJsonValue::Undefined if the key is invalid. In particular, the \c{is...} + functions (for example \l QJsonValue::isString(), \l + QJsonValue::isDouble()) return \c false for QJsonValue::Undefined, so we + can check for existence as well as the correct type in a single lookup. - \snippet serialization/savegame/character.cpp 1 + If a value does not exist in the JSON object, or has the wrong type, we + don't write to the corresponding \c result member, either, thereby + preserving any values the default constructor may have set. This means + default values are centrally defined in one location (the default + constructor) and need not be repeated in serialisation code + (\l{https://en.wikipedia.org/wiki/Don%27t_repeat_yourself}{DRY}). - In the write() function, we do the reverse of the read() function; assign - values from the Character object to the JSON object. As with accessing - values, there are two ways to set values on a QJsonObject: - \l QJsonObject::operator[]() and QJsonObject::insert(). Both will override - any existing value at the given key. + Observe the use of + \l{https://en.cppreference.com/w/cpp/language/if#If_statements_with_initializer} + {C++17 if-with-initializer} to separate scoping and checking of the variable \c v. + This means we can keep the variable name short, because its scope is limited. - Next up is the Level class: + Compare that to the naïve approach using \c QJsonObject::contains(): + + \badcode + if (json.contains("name") && json["name"].isString()) + result.mName = json["name"].toString(); + \endcode + + which, beside being less readable, requires a total of three lookups (no, + the compiler will \e not optimize these into one), so is three times + slower and repeats \c{"name"} three times (violating the DRY principle). + + \snippet serialization/savegame/character.cpp toJson + + In the toJson() function, we do the reverse of the fromJson() function; + assign values from the Character object to a new JSON object we then + return. As with accessing values, there are two ways to set values on a + QJsonObject: \l QJsonObject::operator[]() and \l QJsonObject::insert(). + Both will override any existing value at the given key. + + \section1 The Level Class \snippet serialization/savegame/level.h 0 - We want to have several levels in our game, each with several NPCs, so we - keep a QList of Character objects. We also provide the familiar read() and - write() functions. + We want the levels in our game to each each have several NPCs, so we keep a QList + of Character objects. We also provide the familiar fromJson() and toJson() + functions. - \snippet serialization/savegame/level.cpp 0 + \snippet serialization/savegame/level.cpp fromJson - Containers can be written and read to and from JSON using QJsonArray. In our + Containers can be written to and read from JSON using QJsonArray. In our case, we construct a QJsonArray from the value associated with the key \c "npcs". Then, for each QJsonValue element in the array, we call - toObject() to get the Character's JSON object. The Character object can then - read their JSON and be appended to our NPC array. + toObject() to get the Character's JSON object. Character::fromJson() can + then turn that QJSonObject into a Character object to append to our NPC array. \note \l{Container Classes}{Associate containers} can be written by storing the key in each value object (if it's not already). With this approach, the @@ -94,11 +109,13 @@ element is used as the key to construct the container when reading it back in. - \snippet serialization/savegame/level.cpp 1 + \snippet serialization/savegame/level.cpp toJson - Again, the write() function is similar to the read() function, except + Again, the toJson() function is similar to the fromJson() function, except reversed. + \section1 The Game Class + Having established the Character and Level classes, we can move on to the Game class: @@ -110,26 +127,43 @@ Next, we provide accessors for the player and levels. We then expose three functions: newGame(), saveGame() and loadGame(). - The read() and write() functions are used by saveGame() and loadGame(). + The read() and toJson() functions are used by saveGame() and loadGame(). + + \div{class="admonition note"}\b{Note:} + Despite \c Game being a value class, we assume that the author wants a game to have + identity, much like your main window would have. We therefore don't use a + static fromJson() function, which would create a new object, but a read() + function we can call on existing objects. There's a 1:1 correspondence + between read() and fromJson(), in that one can be implemented in terms of + the other: + + \code + void read(const QJsonObject &json) { *this = fromJson(json); } + static Game fromObject(const QJsonObject &json) { Game g; g.read(json); return g; } + \endcode - \snippet serialization/savegame/game.cpp 0 + We just use what's more convenient for callers of the functions. + \enddiv + + \snippet serialization/savegame/game.cpp newGame To setup a new game, we create the player and populate the levels and their NPCs. - \snippet serialization/savegame/game.cpp 1 + \snippet serialization/savegame/game.cpp read - The first thing we do in the read() function is tell the player to read - itself. We then clear the level array so that calling loadGame() on the - same Game object twice doesn't result in old levels hanging around. + The read() function starts by replacing the player with the + one read from JSON. We then clear() the level array so that calling + loadGame() on the same Game object twice doesn't result in old levels + hanging around. We then populate the level array by reading each Level from a QJsonArray. - \snippet serialization/savegame/game.cpp 2 + \snippet serialization/savegame/game.cpp toJson - We write the game to JSON similarly to how we write Level. + Writing the game to JSON is similar to writing a level. - \snippet serialization/savegame/game.cpp 3 + \snippet serialization/savegame/game.cpp loadGame When loading a saved game in loadGame(), the first thing we do is open the save file based on which format it was saved to; \c "save.json" for JSON, @@ -143,14 +177,16 @@ After constructing the QJsonDocument, we instruct the Game object to read itself and then return \c true to indicate success. - \snippet serialization/savegame/game.cpp 4 + \snippet serialization/savegame/game.cpp saveGame Not surprisingly, saveGame() looks very much like loadGame(). We determine the file extension based on the format, print a warning and return \c false if the opening of the file fails. We then write the Game object to a - QJsonDocument, and call either QJsonDocument::toJson() or to - QJsonDocument::toBinaryData() to save the game, depending on which format - was specified. + QJsonObject. To save the game in the format that was specified, we + convert the JSON object into either a QJsonDocument for a subsequent + QJsonDocument::toJson() call, or a QCborValue for QCborValue::toCbor(). + + \section1 Tying It All Together We are now ready to enter main(): @@ -182,5 +218,5 @@ human-readable JSON files, but you also have the option to use a binary format if it's required, \e without rewriting any code. - \sa {JSON Support in Qt}, {Data Storage} + \sa {JSON Support in Qt}, {CBOR Support in Qt}, {Data Input Output} */ diff --git a/examples/corelib/serialization/savegame/game.cpp b/examples/corelib/serialization/savegame/game.cpp index bb7c2013f2..f99ecb8b51 100644 --- a/examples/corelib/serialization/savegame/game.cpp +++ b/examples/corelib/serialization/savegame/game.cpp @@ -1,52 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "game.h" @@ -58,6 +11,8 @@ #include <QRandomGenerator> #include <QTextStream> +using namespace Qt::StringLiterals; + Character Game::player() const { return mPlayer; @@ -68,52 +23,45 @@ QList<Level> Game::levels() const return mLevels; } -//! [0] +//! [newGame] void Game::newGame() { mPlayer = Character(); - mPlayer.setName(QStringLiteral("Hero")); + mPlayer.setName("Hero"_L1); mPlayer.setClassType(Character::Archer); mPlayer.setLevel(QRandomGenerator::global()->bounded(15, 21)); mLevels.clear(); mLevels.reserve(2); - Level village(QStringLiteral("Village")); + Level village("Village"_L1); QList<Character> villageNpcs; villageNpcs.reserve(2); - villageNpcs.append(Character(QStringLiteral("Barry the Blacksmith"), - QRandomGenerator::global()->bounded(8, 11), - Character::Warrior)); - villageNpcs.append(Character(QStringLiteral("Terry the Trader"), - QRandomGenerator::global()->bounded(6, 8), - Character::Warrior)); + villageNpcs.append(Character("Barry the Blacksmith"_L1, + QRandomGenerator::global()->bounded(8, 11), Character::Warrior)); + villageNpcs.append(Character("Terry the Trader"_L1, + QRandomGenerator::global()->bounded(6, 8), Character::Warrior)); village.setNpcs(villageNpcs); mLevels.append(village); - Level dungeon(QStringLiteral("Dungeon")); + Level dungeon("Dungeon"_L1); QList<Character> dungeonNpcs; dungeonNpcs.reserve(3); - dungeonNpcs.append(Character(QStringLiteral("Eric the Evil"), - QRandomGenerator::global()->bounded(18, 26), - Character::Mage)); - dungeonNpcs.append(Character(QStringLiteral("Eric's Left Minion"), - QRandomGenerator::global()->bounded(5, 7), - Character::Warrior)); - dungeonNpcs.append(Character(QStringLiteral("Eric's Right Minion"), - QRandomGenerator::global()->bounded(4, 9), - Character::Warrior)); + dungeonNpcs.append(Character("Eric the Evil"_L1, + QRandomGenerator::global()->bounded(18, 26), Character::Mage)); + dungeonNpcs.append(Character("Eric's Left Minion"_L1, + QRandomGenerator::global()->bounded(5, 7), Character::Warrior)); + dungeonNpcs.append(Character("Eric's Right Minion"_L1, + QRandomGenerator::global()->bounded(4, 9), Character::Warrior)); dungeon.setNpcs(dungeonNpcs); mLevels.append(dungeon); } -//! [0] +//! [newGame] -//! [3] +//! [loadGame] bool Game::loadGame(Game::SaveFormat saveFormat) { - QFile loadFile(saveFormat == Json - ? QStringLiteral("save.json") - : QStringLiteral("save.dat")); + QFile loadFile(saveFormat == Json ? "save.json"_L1 : "save.dat"_L1); if (!loadFile.open(QIODevice::ReadOnly)) { qWarning("Couldn't open save file."); @@ -123,85 +71,72 @@ bool Game::loadGame(Game::SaveFormat saveFormat) QByteArray saveData = loadFile.readAll(); QJsonDocument loadDoc(saveFormat == Json - ? QJsonDocument::fromJson(saveData) - : QJsonDocument(QCborValue::fromCbor(saveData).toMap().toJsonObject())); + ? QJsonDocument::fromJson(saveData) + : QJsonDocument(QCborValue::fromCbor(saveData).toMap().toJsonObject())); read(loadDoc.object()); - QTextStream(stdout) << "Loaded save for " - << loadDoc["player"]["name"].toString() - << " using " - << (saveFormat != Json ? "CBOR" : "JSON") << "...\n"; + QTextStream(stdout) << "Loaded save for " << loadDoc["player"]["name"].toString() + << " using " << (saveFormat != Json ? "CBOR" : "JSON") << "...\n"; return true; } -//! [3] +//! [loadGame] -//! [4] +//! [saveGame] bool Game::saveGame(Game::SaveFormat saveFormat) const { - QFile saveFile(saveFormat == Json - ? QStringLiteral("save.json") - : QStringLiteral("save.dat")); + QFile saveFile(saveFormat == Json ? "save.json"_L1 : "save.dat"_L1); if (!saveFile.open(QIODevice::WriteOnly)) { qWarning("Couldn't open save file."); return false; } - QJsonObject gameObject; - write(gameObject); - saveFile.write(saveFormat == Json - ? QJsonDocument(gameObject).toJson() - : QCborValue::fromJsonValue(gameObject).toCbor()); + QJsonObject gameObject = toJson(); + saveFile.write(saveFormat == Json ? QJsonDocument(gameObject).toJson() + : QCborValue::fromJsonValue(gameObject).toCbor()); return true; } -//! [4] +//! [saveGame] -//! [1] +//! [read] void Game::read(const QJsonObject &json) { - if (json.contains("player") && json["player"].isObject()) - mPlayer.read(json["player"].toObject()); + if (const QJsonValue v = json["player"]; v.isObject()) + mPlayer = Character::fromJson(v.toObject()); - if (json.contains("levels") && json["levels"].isArray()) { - QJsonArray levelArray = json["levels"].toArray(); + if (const QJsonValue v = json["levels"]; v.isArray()) { + const QJsonArray levels = v.toArray(); mLevels.clear(); - mLevels.reserve(levelArray.size()); - for (int levelIndex = 0; levelIndex < levelArray.size(); ++levelIndex) { - QJsonObject levelObject = levelArray[levelIndex].toObject(); - Level level; - level.read(levelObject); - mLevels.append(level); - } + mLevels.reserve(levels.size()); + for (const QJsonValue &level : levels) + mLevels.append(Level::fromJson(level.toObject())); } } -//! [1] +//! [read] -//! [2] -void Game::write(QJsonObject &json) const +//! [toJson] +QJsonObject Game::toJson() const { - QJsonObject playerObject; - mPlayer.write(playerObject); - json["player"] = playerObject; - - QJsonArray levelArray; - for (const Level &level : mLevels) { - QJsonObject levelObject; - level.write(levelObject); - levelArray.append(levelObject); - } - json["levels"] = levelArray; + QJsonObject json; + json["player"] = mPlayer.toJson(); + + QJsonArray levels; + for (const Level &level : mLevels) + levels.append(level.toJson()); + json["levels"] = levels; + return json; } -//! [2] +//! [toJson] -void Game::print(int indentation) const +void Game::print(QTextStream &s, int indentation) const { const QString indent(indentation * 2, ' '); - QTextStream(stdout) << indent << "Player\n"; - mPlayer.print(indentation + 1); + s << indent << "Player\n"; + mPlayer.print(s, indentation + 1); - QTextStream(stdout) << indent << "Levels\n"; - for (Level level : mLevels) - level.print(indentation + 1); + s << indent << "Levels\n"; + for (const Level &level : mLevels) + level.print(s, indentation + 1); } diff --git a/examples/corelib/serialization/savegame/game.h b/examples/corelib/serialization/savegame/game.h index dfcb27eb1c..5ba5952923 100644 --- a/examples/corelib/serialization/savegame/game.h +++ b/examples/corelib/serialization/savegame/game.h @@ -1,69 +1,22 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef GAME_H #define GAME_H +#include "character.h" +#include "level.h" + #include <QJsonObject> #include <QList> -#include "character.h" -#include "level.h" +QT_FORWARD_DECLARE_CLASS(QTextStream) //! [0] class Game { public: - enum SaveFormat { - Json, Binary - }; + enum SaveFormat { Json, Binary }; Character player() const; QList<Level> levels() const; @@ -73,9 +26,10 @@ public: bool saveGame(SaveFormat saveFormat) const; void read(const QJsonObject &json); - void write(QJsonObject &json) const; + QJsonObject toJson() const; + + void print(QTextStream &s, int indentation = 0) const; - void print(int indentation = 0) const; private: Character mPlayer; QList<Level> mLevels; diff --git a/examples/corelib/serialization/savegame/level.cpp b/examples/corelib/serialization/savegame/level.cpp index ea6ef147b9..f30d35e57f 100644 --- a/examples/corelib/serialization/savegame/level.cpp +++ b/examples/corelib/serialization/savegame/level.cpp @@ -1,61 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "level.h" #include <QJsonArray> #include <QTextStream> -Level::Level(const QString &name) : mName(name) -{ -} +Level::Level(const QString &name) : mName(name) { } QString Level::name() const { @@ -72,46 +23,43 @@ void Level::setNpcs(const QList<Character> &npcs) mNpcs = npcs; } -//! [0] -void Level::read(const QJsonObject &json) +//! [fromJson] +Level Level::fromJson(const QJsonObject &json) { - if (json.contains("name") && json["name"].isString()) - mName = json["name"].toString(); + Level result; + + if (const QJsonValue v = json["name"]; v.isString()) + result.mName = v.toString(); - if (json.contains("npcs") && json["npcs"].isArray()) { - QJsonArray npcArray = json["npcs"].toArray(); - mNpcs.clear(); - mNpcs.reserve(npcArray.size()); - for (int npcIndex = 0; npcIndex < npcArray.size(); ++npcIndex) { - QJsonObject npcObject = npcArray[npcIndex].toObject(); - Character npc; - npc.read(npcObject); - mNpcs.append(npc); - } + if (const QJsonValue v = json["npcs"]; v.isArray()) { + const QJsonArray npcs = v.toArray(); + result.mNpcs.reserve(npcs.size()); + for (const QJsonValue &npc : npcs) + result.mNpcs.append(Character::fromJson(npc.toObject())); } + + return result; } -//! [0] +//! [fromJson] -//! [1] -void Level::write(QJsonObject &json) const +//! [toJson] +QJsonObject Level::toJson() const { + QJsonObject json; json["name"] = mName; QJsonArray npcArray; - for (const Character &npc : mNpcs) { - QJsonObject npcObject; - npc.write(npcObject); - npcArray.append(npcObject); - } + for (const Character &npc : mNpcs) + npcArray.append(npc.toJson()); json["npcs"] = npcArray; + return json; } -//! [1] +//! [toJson] -void Level::print(int indentation) const +void Level::print(QTextStream &s, int indentation) const { const QString indent(indentation * 2, ' '); - QTextStream(stdout) << indent << "Name:\t" << mName << "\n"; - QTextStream(stdout) << indent << "NPCs:\n"; + s << indent << "Name:\t" << mName << "\n" << indent << "NPCs:\n"; for (const Character &character : mNpcs) - character.print(2); + character.print(s, indentation + 1); } diff --git a/examples/corelib/serialization/savegame/level.h b/examples/corelib/serialization/savegame/level.h index ecffde8f22..e487e55ae3 100644 --- a/examples/corelib/serialization/savegame/level.h +++ b/examples/corelib/serialization/savegame/level.h @@ -1,77 +1,33 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef LEVEL_H #define LEVEL_H +#include "character.h" + #include <QJsonObject> #include <QList> -#include "character.h" +QT_FORWARD_DECLARE_CLASS(QTextStream) //! [0] class Level { public: Level() = default; - Level(const QString &name); + explicit Level(const QString &name); QString name() const; QList<Character> npcs() const; void setNpcs(const QList<Character> &npcs); - void read(const QJsonObject &json); - void write(QJsonObject &json) const; + static Level fromJson(const QJsonObject &json); + QJsonObject toJson() const; + + void print(QTextStream &s, int indentation = 0) const; - void print(int indentation = 0) const; private: QString mName; QList<Character> mNpcs; diff --git a/examples/corelib/serialization/savegame/main.cpp b/examples/corelib/serialization/savegame/main.cpp index d091684211..3fc0f3af10 100644 --- a/examples/corelib/serialization/savegame/main.cpp +++ b/examples/corelib/serialization/savegame/main.cpp @@ -1,79 +1,37 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "game.h" #include <QCoreApplication> +#include <QStringList> +#include <QString> #include <QTextStream> -#include "game.h" +using namespace Qt::StringLiterals; // for _L1 + //! [0] int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); - QStringList args = QCoreApplication::arguments(); - bool newGame = true; - if (args.length() > 1) - newGame = (args[1].toLower() != QStringLiteral("load")); - bool json = true; - if (args.length() > 2) - json = (args[2].toLower() != QStringLiteral("binary")); + + const QStringList args = QCoreApplication::arguments(); + const bool newGame + = args.size() <= 1 || QString::compare(args[1], "load"_L1, Qt::CaseInsensitive) != 0; + const bool json + = args.size() <= 2 || QString::compare(args[2], "binary"_L1, Qt::CaseInsensitive) != 0; Game game; if (newGame) game.newGame(); else if (!game.loadGame(json ? Game::Json : Game::Binary)) - return 1; + return 1; // Game is played; changes are made... //! [0] //! [1] - QTextStream(stdout) << "Game ended in the following state:\n"; - game.print(); + QTextStream s(stdout); + s << "Game ended in the following state:\n"; + game.print(s); if (!game.saveGame(json ? Game::Json : Game::Binary)) return 1; diff --git a/examples/corelib/serialization/serialization.pro b/examples/corelib/serialization/serialization.pro index 7651444f19..e20fcb57fd 100644 --- a/examples/corelib/serialization/serialization.pro +++ b/examples/corelib/serialization/serialization.pro @@ -3,3 +3,7 @@ SUBDIRS = \ cbordump \ convert \ savegame + +qtHaveModule(widgets) { + SUBDIRS += streambookmarks +} diff --git a/examples/corelib/serialization/streambookmarks/CMakeLists.txt b/examples/corelib/serialization/streambookmarks/CMakeLists.txt new file mode 100644 index 0000000000..bad55fa661 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/CMakeLists.txt @@ -0,0 +1,40 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(streambookmarks LANGUAGES CXX) + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) + +qt_standard_project_setup() + +qt_add_executable(streambookmarks + main.cpp + mainwindow.cpp mainwindow.h + xbelreader.cpp xbelreader.h + xbelwriter.cpp xbelwriter.h +) + +set_target_properties(streambookmarks PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(streambookmarks PRIVATE + Qt6::Core + Qt6::Gui + Qt6::Widgets +) + +install(TARGETS streambookmarks + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_app_script( + TARGET streambookmarks + OUTPUT_SCRIPT deploy_script + NO_UNSUPPORTED_PLATFORM_ERROR +) +install(SCRIPT ${deploy_script}) diff --git a/examples/corelib/serialization/streambookmarks/doc/images/filemenu.png b/examples/corelib/serialization/streambookmarks/doc/images/filemenu.png Binary files differnew file mode 100644 index 0000000000..1a92895ccf --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/doc/images/filemenu.png diff --git a/examples/corelib/serialization/streambookmarks/doc/images/helpmenu.png b/examples/corelib/serialization/streambookmarks/doc/images/helpmenu.png Binary files differnew file mode 100644 index 0000000000..baa98bee96 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/doc/images/helpmenu.png diff --git a/examples/corelib/serialization/streambookmarks/doc/images/screenshot.png b/examples/corelib/serialization/streambookmarks/doc/images/screenshot.png Binary files differnew file mode 100644 index 0000000000..422873b6d0 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/doc/images/screenshot.png diff --git a/examples/corelib/serialization/streambookmarks/doc/src/qxmlstreambookmarks.qdoc b/examples/corelib/serialization/streambookmarks/doc/src/qxmlstreambookmarks.qdoc new file mode 100644 index 0000000000..8e32dd8d0b --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/doc/src/qxmlstreambookmarks.qdoc @@ -0,0 +1,223 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \example serialization/streambookmarks + \examplecategory {Data Processing & I/O} + \meta tag {network} + \title QXmlStream Bookmarks Example + \brief Demonstrates how to read and write XBEL files. + \ingroup xml-examples + + The QXmlStream Bookmarks example provides a viewer for XML Bookmark Exchange + Language (XBEL) files. It can read bookmarks using Qt's QXmlStreamReader and + write them back out again using QXmlStreamWriter. As this example aims to + show how to use these reader and writer types, it provides no means to open + a bookmark, add a new one, or merge two bookmark files, and only minimal + scope for editing bookmarks. None the less, it could surely be extended with + such features, if desired. + + \image screenshot.png + + \section1 XbelWriter Class Definition + + The \c XbelWriter class takes a \l{QTreeWidget}{tree widget} describing a + hierarchy of folders containing bookmarks. Its \c writeFile() provides the + means to write out this hierarchy, in XBEL format, to a given output device. + + Internally, it records the tree widget it was given and packages a private + instance of QXmlStreamWriter, which provides it with the means to stream + XML. It has an internal \c writeItem() to write each item in its tree. + + \snippet serialization/streambookmarks/xbelwriter.h 0 + + \section1 XbelWriter Class Implementation + + The \c XbelWriter constructor accepts the \a treeWidget it will describe. It + stores that and enables \l{QXmlStreamWriter}'s auto-formatting property. + This last splits the data into several lines, with indentation to indicate + the structure of the tree, which makes the XML output easier to read. + + \snippet serialization/streambookmarks/xbelwriter.cpp 0 + + The \c writeFile() function accepts a QIODevice object and directs its + QXmlStreamWriter member to write to this device, using \c setDevice(). This + function then writes the document type definition(DTD), the start element, + the version, and delegates writing of each of the \c{treeWidget}'s top-level + items to \c writeItem(). Finally, it closes the document and returns. + + \snippet serialization/streambookmarks/xbelwriter.cpp 1 + + The \c writeItem() function accepts a QTreeWidgetItem object and writes to + its XML stream a representation of the object, which depends on its \c + UserRole, which can be one of a \c{"folder"}, \c{"bookmark"}, + or \c{"separator"}. Within each folder, it calls itself recursively on each + child item, to recursively include a representation of each child within the + folder's XML element. + + \snippet serialization/streambookmarks/xbelwriter.cpp 2 + + \section1 XbelReader Class Definition + + The \c XbelReader takes a \l{QTreeWidget}{tree widget} to populate with + items describing a bookmark hierarchy. It supports reading XBEL data from a + QIODevice as a source of these items. If parsing of the XBEL data fails, it + can report what went wrong. + + Internally, it records the QTreeWidget that it will populate and packages an + instance of QXmlStreamReader, the companion class to QXmlStreamWriter, which + it will use to read XBEL data. + + \snippet serialization/streambookmarks/xbelreader.h 0 + + \section1 XbelReader Class Implementation + + Since the XBEL reader is only concerned with reading XML elements, it makes + extensive use of the \l{QXmlStreamReader::}{readNextStartElement()} + convenience function. + + The \c XbelReader constructor requires a QTreeWidget that it will populate. + It populates the tree widget's style with suitable icons: a folder icon that + changes form to indicate whether each folder as open or closed; and a + standard file icon for the individual bookmarks within those folders. + + \snippet serialization/streambookmarks/xbelreader.cpp 0 + + The \c read() function accepts a QIODevice. It directs its QXmlStreamReader + member to read content from that device. Note that the XML input must be + well-formed to be accepted by QXmlStreamReader. First it reads the outer + structure and verifies the content is an XBEL 1.0 file; if it is, \c read() + delegates the actual reading of content to the internal \c readXBEL(). + + Otherwise, the \l{QXmlStreamReader::}{raiseError()} function is used to + record an error message. The reader itself may also do the same if it + encounters errors in the input. When \c read() has finished, it returns + true if there were no errors. + + \snippet serialization/streambookmarks/xbelreader.cpp 1 + + If \c read() returns false, its caller can obtain a description of the + error, complete with line and column number within the stream, by calling + the \c errorString() function. + + \snippet serialization/streambookmarks/xbelreader.cpp 2 + + The \c readXBEL() function reads the name of a startElement and calls the + appropriate function to read it, depending on whether if its tag name + is \c{"folder"}, \c{"bookmark"} or \c{"separator"}. Any other elements + encountered are skipped. The function starts with a precondition, verifying + that the XML reader has just opened an \c{"xbel"} element. + + \snippet serialization/streambookmarks/xbelreader.cpp 3 + + The \c readBookmark() function creates a new editable item representing a + single bookmark. It records the XML \c{"href"} attribute of the current + element as second column text of the item and provisionally sets its first + column text to \c{"Unknown title"} before scanning the rest of the element + for a title element to over-ride that, skipping any unrecognized child + elements. + + \snippet serialization/streambookmarks/xbelreader.cpp 5 + + The \c readTitle() function reads a bookmark's title and records it as the + title (first column text) of the item for which it was called. + + \snippet serialization/streambookmarks/xbelreader.cpp 6 + + The \c readSeparator() function creates a separator and sets its flags. The + separator item's text is set to 30 centered dots. The rest of the element is + then skipped using \l{QXmlStreamReader::}{skipCurrentElement()}. + + \snippet serialization/streambookmarks/xbelreader.cpp 6 + + The \c readFolder() function creates an item and iterates the content of the + folder element, adding children to this item to represent the contents of + the folder element. The loop over folder content is similar in form to the + one in \c readXBEL(), save that it now accepts a title element to set the + title of the folder. + + \snippet serialization/streambookmarks/xbelreader.cpp 7 + + The \c createChildItem() helper function creates a new tree widget item + that's either a child of the given item or, if no parent item is given, a + direct child of the tree widget. It sets the new item's \c UserRole to the + tag name of the current XML element, matching how XbelWriter::writeFile() + uses that \c UserRole. + + \snippet serialization/streambookmarks/xbelreader.cpp 8 + + \section1 MainWindow Class Definition + + The \c MainWindow class is a subclass of QMainWindow, with a \c File menu + and a \c Help menu. + + \snippet serialization/streambookmarks/mainwindow.h 0 + + \section1 MainWindow Class Implementation + + The \c MainWindow constructor sets up its QTreeWidget object, \c treeWidget, + as its own central widget, with column headings for the title and location + of each book-mark. It configures a custom menu that enables the user to + perform actions on individual bookmarks within the tree widget. + + It invokes \c createMenus() to set up its own menus and their corresponding + actions. It sets its title, announces itself as ready and sets its size to a + reasonable proportion of the available screen space. + + \snippet serialization/streambookmarks/mainwindow.cpp 0 + + A custom menu, triggered when the user right-clicks on a bookmark, provides + for copying the bookmark as a link or directing a desktop browser to open + the URL it references. This menu is implemented (when relevant features are + enabled) by \c onCustomContextMenuRequested(). + + \snippet serialization/streambookmarks/mainwindow.cpp 1 + + The \c createMenus() function creates the \c fileMenu and \c helpMenu and + adds QAction objects to them, bound variously to the \c open(), \c saveAs() + and \c about() functions, along with QWidget::close() and + QApplication::aboutQt(). The connections are as shown below: + + \snippet serialization/streambookmarks/mainwindow.cpp 2 + + This creates the menu shown in the screenshots below: + + \table + \row + \li \inlineimage filemenu.png + \li \inlineimage helpmenu.png + \endtable + + The \c open() function, when triggered, offers the user a file dialog to use + to select a bookmarks file. If a file is selected, it is parsed using an \c + XBelReader to populate the \c treeWidget with bookmarks. If problems arise + with opening or parsing the file, a suitable warning message is displayed to + the user, including file name and error message. Otherwise, the bookmarks + read from the file are displayed and the window's status bar briefly reports + that the file has been loaded. + + \snippet serialization/streambookmarks/mainwindow.cpp 3 + + The \c saveAs() function displays a QFileDialog, prompting the user for a \c + fileName, to which to save a copy of the bookmarks data. Similar to the \c + open() function, this function also displays a warning message if the file + cannot be written to. + + \snippet serialization/streambookmarks/mainwindow.cpp 4 + + The \c about() function displays a QMessageBox with a brief description of + the example, or general information about Qt and the version of it in use. + + \snippet serialization/streambookmarks/mainwindow.cpp 5 + + \section1 \c{main()} Function + + The \c main() function instantiates \c MainWindow and invokes the \c show() + function to display it, then its \c open(), as this is most likely what the + user shall want to do first. + + \snippet serialization/streambookmarks/main.cpp 0 + + See the \l{https://pyxml.sourceforge.net/topics/xbel/} {XML Bookmark + Exchange Language Resource Page} for more information about XBEL files. +*/ diff --git a/examples/corelib/serialization/streambookmarks/jennifer.xbel b/examples/corelib/serialization/streambookmarks/jennifer.xbel new file mode 100644 index 0000000000..d504236830 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/jennifer.xbel @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE xbel> +<xbel version="1.0"> + <folder folded="no"> + <title>Qt Resources</title> + <bookmark href="https://www.qt.io/"> + <title>Qt home page</title> + </bookmark> + <bookmark href="https://www.qt.io/contact-us/partners"> + <title>Qt Partners</title> + </bookmark> + <bookmark href="https://www.qt.io/qt-professional-services"> + <title>Professional Services</title> + </bookmark> + <bookmark href="https://doc.qt.io/"> + <title>Qt Documentation</title> + </bookmark> + <folder folded="yes"> + <title>Community Resources</title> + <bookmark href="https://contribute.qt-project.org"> + <title>The Qt Project</title> + </bookmark> + <bookmark href="https://www.qtcentre.org/content/"> + <title>Qt Centre</title> + </bookmark> + <bookmark href="https://forum.qt.io/"> + <title>Forum.Qt.org</title> + </bookmark> + <bookmark href="https://digitalfanatics.org/projects/qt_tutorial/"> + <title>The Independent Qt Tutorial</title> + </bookmark> + <bookmark href="https://www.qtforum.de/"> + <title>German Qt Forum</title> + </bookmark> + <bookmark href="https://www.qt-dev.com/"> + <title>Korean Qt Community Site</title> + </bookmark> + <bookmark href="http://www.prog.org.ru/"> + <title>Russian Qt Forum</title> + </bookmark> + </folder> + </folder> + <folder folded="no"> + <title>Online Dictionaries</title> + <bookmark href="https://www.dictionary.com/"> + <title>Dictionary.com</title> + </bookmark> + <bookmark href="https://www.merriam-webster.com/"> + <title>Merriam-Webster Online</title> + </bookmark> + <bookmark href="https://dictionary.cambridge.org/"> + <title>Cambridge Dictionaries Online</title> + </bookmark> + <bookmark href="https://www.onelook.com/"> + <title>OneLook Dictionary Search</title> + </bookmark> + <separator/> + <bookmark href="https://dict.tu-chemnitz.de/"> + <title>BEOLINGUS, a service of TU Chemnitz</title> + </bookmark> + <separator/> + <bookmark href="http://atilf.atilf.fr/tlf.htm"> + <title>Trésor de la Langue Française informatisé</title> + </bookmark> + <bookmark href="https://www.dictionnaire-academie.fr/"> + <title>Dictionnaire de l'Académie Française</title> + </bookmark> + </folder> +</xbel> diff --git a/examples/corelib/serialization/streambookmarks/main.cpp b/examples/corelib/serialization/streambookmarks/main.cpp new file mode 100644 index 0000000000..0fd317de43 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/main.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "mainwindow.h" + +#include <QApplication> + +//! [0] +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + MainWindow mainWin; + mainWin.show(); + mainWin.open(); + return app.exec(); +} +//! [0] diff --git a/examples/corelib/serialization/streambookmarks/mainwindow.cpp b/examples/corelib/serialization/streambookmarks/mainwindow.cpp new file mode 100644 index 0000000000..a863f77ab7 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/mainwindow.cpp @@ -0,0 +1,151 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "mainwindow.h" +#include "xbelreader.h" +#include "xbelwriter.h" + +#include <QFileDialog> +#include <QHeaderView> +#include <QMenuBar> +#include <QMessageBox> +#include <QStatusBar> +#include <QTreeWidget> + +#include <QAction> +#if QT_CONFIG(clipboard) +# include <QClipboard> +#endif +#include <QDesktopServices> +#include <QApplication> +#include <QScreen> + +using namespace Qt::StringLiterals; + +//! [0] +MainWindow::MainWindow() : treeWidget(new QTreeWidget) +{ + treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch); + treeWidget->setHeaderLabels(QStringList{tr("Title"), tr("Location")}); +#if QT_CONFIG(clipboard) && QT_CONFIG(contextmenu) + treeWidget->setContextMenuPolicy(Qt::CustomContextMenu); + connect(treeWidget, &QWidget::customContextMenuRequested, + this, &MainWindow::onCustomContextMenuRequested); +#endif + setCentralWidget(treeWidget); + + createMenus(); + + statusBar()->showMessage(tr("Ready")); + + setWindowTitle(tr("QXmlStream Bookmarks")); + const QSize availableSize = screen()->availableGeometry().size(); + resize(availableSize.width() / 2, availableSize.height() / 3); +} +//! [0] + +//! [1] +#if QT_CONFIG(clipboard) && QT_CONFIG(contextmenu) +void MainWindow::onCustomContextMenuRequested(const QPoint &pos) +{ + const QTreeWidgetItem *item = treeWidget->itemAt(pos); + if (!item) + return; + const QString url = item->text(1); + QMenu contextMenu; + QAction *copyAction = contextMenu.addAction(tr("Copy Link to Clipboard")); + QAction *openAction = contextMenu.addAction(tr("Open")); + QAction *action = contextMenu.exec(treeWidget->viewport()->mapToGlobal(pos)); + if (action == copyAction) + QGuiApplication::clipboard()->setText(url); + else if (action == openAction) + QDesktopServices::openUrl(QUrl(url)); +} +#endif // QT_CONFIG(clipboard) && QT_CONFIG(contextmenu) +//! [1] + +//! [2] +void MainWindow::createMenus() +{ + QMenu *fileMenu = menuBar()->addMenu(tr("&File")); + QAction *openAct = fileMenu->addAction(tr("&Open..."), this, &MainWindow::open); + openAct->setShortcuts(QKeySequence::Open); + + QAction *saveAsAct = fileMenu->addAction(tr("&Save As..."), this, &MainWindow::saveAs); + saveAsAct->setShortcuts(QKeySequence::SaveAs); + + QAction *exitAct = fileMenu->addAction(tr("E&xit"), this, &QWidget::close); + exitAct->setShortcuts(QKeySequence::Quit); + + menuBar()->addSeparator(); + + QMenu *helpMenu = menuBar()->addMenu(tr("&Help")); + helpMenu->addAction(tr("&About"), this, &MainWindow::about); + helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt); +} +//! [2] + +//! [3] +void MainWindow::open() +{ + QFileDialog fileDialog(this, tr("Open Bookmark File"), QDir::currentPath()); + fileDialog.setMimeTypeFilters({"application/x-xbel"_L1}); + if (fileDialog.exec() != QDialog::Accepted) + return; + + treeWidget->clear(); + + const QString fileName = fileDialog.selectedFiles().constFirst(); + QFile file(fileName); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + QMessageBox::warning(this, tr("QXmlStream Bookmarks"), + tr("Cannot read file %1:\n%2.") + .arg(QDir::toNativeSeparators(fileName), file.errorString())); + return; + } + + XbelReader reader(treeWidget); + if (!reader.read(&file)) { + QMessageBox::warning( + this, tr("QXmlStream Bookmarks"), + tr("Parse error in file %1:\n\n%2") + .arg(QDir::toNativeSeparators(fileName), reader.errorString())); + } else { + statusBar()->showMessage(tr("File loaded"), 2000); + } +} +//! [3] + +//! [4] +void MainWindow::saveAs() +{ + QFileDialog fileDialog(this, tr("Save Bookmark File"), QDir::currentPath()); + fileDialog.setAcceptMode(QFileDialog::AcceptSave); + fileDialog.setDefaultSuffix("xbel"_L1); + fileDialog.setMimeTypeFilters({"application/x-xbel"_L1}); + if (fileDialog.exec() != QDialog::Accepted) + return; + + const QString fileName = fileDialog.selectedFiles().constFirst(); + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + QMessageBox::warning(this, tr("QXmlStream Bookmarks"), + tr("Cannot write file %1:\n%2.") + .arg(QDir::toNativeSeparators(fileName), file.errorString())); + return; + } + + XbelWriter writer(treeWidget); + if (writer.writeFile(&file)) + statusBar()->showMessage(tr("File saved"), 2000); +} +//! [4] + +//! [5] +void MainWindow::about() +{ + QMessageBox::about(this, tr("About QXmlStream Bookmarks"), + tr("The <b>QXmlStream Bookmarks</b> example demonstrates how to use Qt's " + "QXmlStream classes to read and write XML documents.")); +} +//! [5] diff --git a/examples/corelib/serialization/streambookmarks/mainwindow.h b/examples/corelib/serialization/streambookmarks/mainwindow.h new file mode 100644 index 0000000000..d9efe6b5a5 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/mainwindow.h @@ -0,0 +1,35 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> + +QT_BEGIN_NAMESPACE +class QTreeWidget; +QT_END_NAMESPACE + +//! [0] +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(); + +public slots: + void open(); + void saveAs(); + void about(); +#if QT_CONFIG(clipboard) && QT_CONFIG(contextmenu) + void onCustomContextMenuRequested(const QPoint &pos); +#endif +private: + void createMenus(); + + QTreeWidget *const treeWidget; +}; +//! [0] + +#endif diff --git a/examples/corelib/serialization/streambookmarks/streambookmarks.pro b/examples/corelib/serialization/streambookmarks/streambookmarks.pro new file mode 100644 index 0000000000..34d2caae82 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/streambookmarks.pro @@ -0,0 +1,15 @@ +HEADERS = mainwindow.h \ + xbelreader.h \ + xbelwriter.h +SOURCES = main.cpp \ + mainwindow.cpp \ + xbelreader.cpp \ + xbelwriter.cpp +QT += widgets +requires(qtConfig(filedialog)) + +EXAMPLE_FILES = jennifer.xbel + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/corelib/serialization/streambookmarks +INSTALLS += target diff --git a/examples/corelib/serialization/streambookmarks/xbelreader.cpp b/examples/corelib/serialization/streambookmarks/xbelreader.cpp new file mode 100644 index 0000000000..c622cf6642 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/xbelreader.cpp @@ -0,0 +1,140 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "xbelreader.h" + +#include <QStyle> +#include <QTreeWidget> + +using namespace Qt::StringLiterals; + +//! [0] +XbelReader::XbelReader(QTreeWidget *treeWidget) : treeWidget(treeWidget) +{ + QStyle *style = treeWidget->style(); + + folderIcon.addPixmap(style->standardPixmap(QStyle::SP_DirClosedIcon), QIcon::Normal, + QIcon::Off); + folderIcon.addPixmap(style->standardPixmap(QStyle::SP_DirOpenIcon), QIcon::Normal, QIcon::On); + bookmarkIcon.addPixmap(style->standardPixmap(QStyle::SP_FileIcon)); +} +//! [0] + +//! [1] +bool XbelReader::read(QIODevice *device) +{ + xml.setDevice(device); + + if (xml.readNextStartElement()) { + if (xml.name() == "xbel"_L1 && xml.attributes().value("version"_L1) == "1.0"_L1) + readXBEL(); + else + xml.raiseError(QObject::tr("The file is not an XBEL version 1.0 file.")); + } + + return !xml.error(); +} +//! [1] + +//! [2] +QString XbelReader::errorString() const +{ + return QObject::tr("%1\nLine %2, column %3") + .arg(xml.errorString()) + .arg(xml.lineNumber()) + .arg(xml.columnNumber()); +} +//! [2] + +//! [3] +void XbelReader::readXBEL() +{ + Q_ASSERT(xml.isStartElement() && xml.name() == "xbel"_L1); + + while (xml.readNextStartElement()) { + if (xml.name() == "folder"_L1) + readFolder(nullptr); + else if (xml.name() == "bookmark"_L1) + readBookmark(nullptr); + else if (xml.name() == "separator"_L1) + readSeparator(nullptr); + else + xml.skipCurrentElement(); + } +} +//! [3] + +//! [4] +void XbelReader::readBookmark(QTreeWidgetItem *item) +{ + Q_ASSERT(xml.isStartElement() && xml.name() == "bookmark"_L1); + + QTreeWidgetItem *bookmark = createChildItem(item); + bookmark->setFlags(bookmark->flags() | Qt::ItemIsEditable); + bookmark->setIcon(0, bookmarkIcon); + bookmark->setText(0, QObject::tr("Unknown title")); + bookmark->setText(1, xml.attributes().value("href"_L1).toString()); + + while (xml.readNextStartElement()) { + if (xml.name() == "title"_L1) + readTitle(bookmark); + else + xml.skipCurrentElement(); + } +} +//! [4] + +//! [5] +void XbelReader::readTitle(QTreeWidgetItem *item) +{ + Q_ASSERT(xml.isStartElement() && xml.name() == "title"_L1); + item->setText(0, xml.readElementText()); +} +//! [5] + +//! [6] +void XbelReader::readSeparator(QTreeWidgetItem *item) +{ + Q_ASSERT(xml.isStartElement() && xml.name() == "separator"_L1); + constexpr char16_t midDot = u'\xB7'; + static const QString dots(30, midDot); + + QTreeWidgetItem *separator = createChildItem(item); + separator->setFlags(item ? item->flags() & ~Qt::ItemIsSelectable : Qt::ItemFlags{}); + separator->setText(0, dots); + xml.skipCurrentElement(); +} +//! [6] + +//! [7] +void XbelReader::readFolder(QTreeWidgetItem *item) +{ + Q_ASSERT(xml.isStartElement() && xml.name() == "folder"_L1); + + QTreeWidgetItem *folder = createChildItem(item); + bool folded = xml.attributes().value("folded"_L1) != "no"_L1; + folder->setExpanded(!folded); + + while (xml.readNextStartElement()) { + if (xml.name() == "title"_L1) + readTitle(folder); + else if (xml.name() == "folder"_L1) + readFolder(folder); + else if (xml.name() == "bookmark"_L1) + readBookmark(folder); + else if (xml.name() == "separator"_L1) + readSeparator(folder); + else + xml.skipCurrentElement(); + } +} +//! [7] + +//! [8] +QTreeWidgetItem *XbelReader::createChildItem(QTreeWidgetItem *item) +{ + QTreeWidgetItem *childItem = item ? new QTreeWidgetItem(item) : new QTreeWidgetItem(treeWidget); + childItem->setData(0, Qt::UserRole, xml.name().toString()); + return childItem; +} +//! [8] diff --git a/examples/corelib/serialization/streambookmarks/xbelreader.h b/examples/corelib/serialization/streambookmarks/xbelreader.h new file mode 100644 index 0000000000..a3fa59d813 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/xbelreader.h @@ -0,0 +1,45 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef XBELREADER_H +#define XBELREADER_H + +#include <QIcon> +#include <QXmlStreamReader> + +QT_BEGIN_NAMESPACE +class QTreeWidget; +class QTreeWidgetItem; +QT_END_NAMESPACE + +//! [0] +class XbelReader +{ +public: +//! [1] + XbelReader(QTreeWidget *treeWidget); +//! [1] + + bool read(QIODevice *device); + QString errorString() const; + +private: +//! [2] + void readXBEL(); + void readTitle(QTreeWidgetItem *item); + void readSeparator(QTreeWidgetItem *item); + void readFolder(QTreeWidgetItem *item); + void readBookmark(QTreeWidgetItem *item); + + QTreeWidgetItem *createChildItem(QTreeWidgetItem *item); + + QXmlStreamReader xml; + QTreeWidget *treeWidget; +//! [2] + + QIcon folderIcon; + QIcon bookmarkIcon; +}; +//! [0] + +#endif diff --git a/examples/corelib/serialization/streambookmarks/xbelwriter.cpp b/examples/corelib/serialization/streambookmarks/xbelwriter.cpp new file mode 100644 index 0000000000..e50f47a5a5 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/xbelwriter.cpp @@ -0,0 +1,56 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "xbelwriter.h" + +#include <QTreeWidget> + +using namespace Qt::StringLiterals; + +//! [0] +XbelWriter::XbelWriter(const QTreeWidget *treeWidget) : treeWidget(treeWidget) +{ + xml.setAutoFormatting(true); +} +//! [0] + +//! [1] +bool XbelWriter::writeFile(QIODevice *device) +{ + xml.setDevice(device); + + xml.writeStartDocument(); + xml.writeDTD("<!DOCTYPE xbel>"_L1); + xml.writeStartElement("xbel"_L1); + xml.writeAttribute("version"_L1, "1.0"_L1); + for (int i = 0; i < treeWidget->topLevelItemCount(); ++i) + writeItem(treeWidget->topLevelItem(i)); + + xml.writeEndDocument(); + return true; +} +//! [1] + +//! [2] +void XbelWriter::writeItem(const QTreeWidgetItem *item) +{ + QString tagName = item->data(0, Qt::UserRole).toString(); + if (tagName == "folder"_L1) { + bool folded = !item->isExpanded(); + xml.writeStartElement(tagName); + xml.writeAttribute("folded"_L1, folded ? "yes"_L1 : "no"_L1); + xml.writeTextElement("title"_L1, item->text(0)); + for (int i = 0; i < item->childCount(); ++i) + writeItem(item->child(i)); + xml.writeEndElement(); + } else if (tagName == "bookmark"_L1) { + xml.writeStartElement(tagName); + if (!item->text(1).isEmpty()) + xml.writeAttribute("href"_L1, item->text(1)); + xml.writeTextElement("title"_L1, item->text(0)); + xml.writeEndElement(); + } else if (tagName == "separator"_L1) { + xml.writeEmptyElement(tagName); + } +} +//! [2] diff --git a/examples/corelib/serialization/streambookmarks/xbelwriter.h b/examples/corelib/serialization/streambookmarks/xbelwriter.h new file mode 100644 index 0000000000..ec95315c4b --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/xbelwriter.h @@ -0,0 +1,28 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef XBELWRITER_H +#define XBELWRITER_H + +#include <QXmlStreamWriter> + +QT_BEGIN_NAMESPACE +class QTreeWidget; +class QTreeWidgetItem; +QT_END_NAMESPACE + +//! [0] +class XbelWriter +{ +public: + explicit XbelWriter(const QTreeWidget *treeWidget); + bool writeFile(QIODevice *device); + +private: + void writeItem(const QTreeWidgetItem *item); + QXmlStreamWriter xml; + const QTreeWidget *treeWidget; +}; +//! [0] + +#endif |