diff options
104 files changed, 7964 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..59e8a79 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +Makefile +Makefile.* +release/ +debug/ +*.o +*.so.* +*.dll +*.lib +*.so +moc_* +*.moc +*.pro.user + +rep_*_replicant.h +rep_*_prime.h + +/repc/repc diff --git a/.qmake.conf b/.qmake.conf new file mode 100644 index 0000000..889750c --- /dev/null +++ b/.qmake.conf @@ -0,0 +1,3 @@ +load(qt_build_config) +CONFIG += qt_example_installs +MODULE_VERSION = 5.4.0 diff --git a/examples/RemoteObjects/ClientApp/ClientApp.desktop b/examples/RemoteObjects/ClientApp/ClientApp.desktop new file mode 100644 index 0000000..e85a515 --- /dev/null +++ b/examples/RemoteObjects/ClientApp/ClientApp.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Encoding=UTF-8 +Version=1.0 +Type=Application +Terminal=false +Name=ClientApp +Exec=/opt/ClientApp/bin/ClientApp +Icon=ClientApp64 +X-Window-Icon= +X-HildonDesk-ShowInToolbar=true +X-Osso-Type=application/x-executable diff --git a/examples/RemoteObjects/ClientApp/ClientApp.pro b/examples/RemoteObjects/ClientApp/ClientApp.pro new file mode 100644 index 0000000..1dc091c --- /dev/null +++ b/examples/RemoteObjects/ClientApp/ClientApp.pro @@ -0,0 +1,13 @@ +SOURCES += main.cpp + +RESOURCES += \ + clientapp.qrc + +QT += remoteobjects quick + +contains(QT_CONFIG, c++11): CONFIG += c++11 + +target.path = $$[QT_INSTALL_EXAMPLES]/RemoteObjects/ClientApp + +INSTALLS += target + diff --git a/examples/RemoteObjects/ClientApp/ClientApp64.png b/examples/RemoteObjects/ClientApp/ClientApp64.png Binary files differnew file mode 100644 index 0000000..707d5c4 --- /dev/null +++ b/examples/RemoteObjects/ClientApp/ClientApp64.png diff --git a/examples/RemoteObjects/ClientApp/ClientApp80.png b/examples/RemoteObjects/ClientApp/ClientApp80.png Binary files differnew file mode 100644 index 0000000..6ad8096 --- /dev/null +++ b/examples/RemoteObjects/ClientApp/ClientApp80.png diff --git a/examples/RemoteObjects/ClientApp/ClientApp_harmattan.desktop b/examples/RemoteObjects/ClientApp/ClientApp_harmattan.desktop new file mode 100644 index 0000000..d3da899 --- /dev/null +++ b/examples/RemoteObjects/ClientApp/ClientApp_harmattan.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Encoding=UTF-8 +Version=1.0 +Type=Application +Terminal=false +Name=ClientApp +Exec=/usr/bin/single-instance /opt/ClientApp/bin/ClientApp +Icon=/usr/share/icons/hicolor/80x80/apps/ClientApp80.png +X-Window-Icon= +X-HildonDesk-ShowInToolbar=true +X-Osso-Type=application/x-executable diff --git a/examples/RemoteObjects/ClientApp/clientapp.qrc b/examples/RemoteObjects/ClientApp/clientapp.qrc new file mode 100644 index 0000000..7e6bf64 --- /dev/null +++ b/examples/RemoteObjects/ClientApp/clientapp.qrc @@ -0,0 +1,9 @@ +<RCC> + <qresource prefix="/qml"> + <file>qml/plugins0.qml</file> + <file>qml/plugins.qml</file> + <file>qml/plugins1.qml</file> + <file>qml/plugins2.qml</file> + </qresource> + <qresource prefix="/images"/> +</RCC> diff --git a/examples/RemoteObjects/ClientApp/main.cpp b/examples/RemoteObjects/ClientApp/main.cpp new file mode 100644 index 0000000..0721f50 --- /dev/null +++ b/examples/RemoteObjects/ClientApp/main.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/QGuiApplication> +#include <QtQml/QQmlEngine> +#include <QtQuick/QQuickView> + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + QQuickView viewer; + viewer.engine()->addImportPath(QStringLiteral("qrc:/qml")); + viewer.setSource(QUrl(QStringLiteral("qrc:/qml/qml/plugins.qml"))); + viewer.show(); + + return app.exec(); +} diff --git a/examples/RemoteObjects/ClientApp/qml/plugins.qml b/examples/RemoteObjects/ClientApp/qml/plugins.qml new file mode 100644 index 0000000..87318a6 --- /dev/null +++ b/examples/RemoteObjects/ClientApp/qml/plugins.qml @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + width: 200 + height: 400 + property int counter: 0; + MouseArea { + anchors.fill: parent + onClicked: + { + counter = (counter + 1) % 3; + console.log(counter); + pageLoader.source = "plugins"+counter+".qml" + } + } + Loader { + id: pageLoader + source: "plugins0.qml" + } +} +//![0] diff --git a/examples/RemoteObjects/ClientApp/qml/plugins0.qml b/examples/RemoteObjects/ClientApp/qml/plugins0.qml new file mode 100644 index 0000000..958ae9f --- /dev/null +++ b/examples/RemoteObjects/ClientApp/qml/plugins0.qml @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 Digia Plc and its Subsidiary(-ies) 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$ +** +****************************************************************************/ +//![0] +import QtQuick 2.0 + +Rectangle { + width: 200 + height: 400 + color: "blue" +} +//![0] diff --git a/examples/RemoteObjects/ClientApp/qml/plugins1.qml b/examples/RemoteObjects/ClientApp/qml/plugins1.qml new file mode 100644 index 0000000..c9a9237 --- /dev/null +++ b/examples/RemoteObjects/ClientApp/qml/plugins1.qml @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 Digia Plc and its Subsidiary(-ies) 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$ +** +****************************************************************************/ +//![0] +import QtQuick 2.0 +import TimeExample 1.0 // import types from the plugin + +Rectangle { + width: 200 + height: 400 + color: "blue" + Clock { // this class is defined in QML (imports/TimeExample/Clock.qml) + id: clock1 + anchors.top: parent.top + Time { // this class is defined in C++ (plugin.cpp) + id: time + } + + hours: time.hour + minutes: time.minute + + } +} +//![0] diff --git a/examples/RemoteObjects/ClientApp/qml/plugins2.qml b/examples/RemoteObjects/ClientApp/qml/plugins2.qml new file mode 100644 index 0000000..55acb8a --- /dev/null +++ b/examples/RemoteObjects/ClientApp/qml/plugins2.qml @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 Digia Plc and its Subsidiary(-ies) 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$ +** +****************************************************************************/ +//![0] +import QtQuick 2.0 +import TimeExample 1.0 // import types from the plugin + +Rectangle { + width: 200 + height: 400 + Clock { // this class is defined in QML (imports/TimeExample/Clock.qml) + id: clock1 + anchors.top: parent.top + Time { // this class is defined in C++ (plugin.cpp) + id: time + } + + hours: time.hour + minutes: time.minute + + } + Clock { // this class is defined in QML (imports/TimeExample/Clock.qml) + id: clock2 + anchors.top: clock1.bottom + Time { // this class is defined in C++ (plugin.cpp) + id: time2 + } + + hours: time2.hour + minutes: time2.minute + + } + +} +//![0] diff --git a/examples/RemoteObjects/CppClient/CppClient.pro b/examples/RemoteObjects/CppClient/CppClient.pro new file mode 100644 index 0000000..05a89b9 --- /dev/null +++ b/examples/RemoteObjects/CppClient/CppClient.pro @@ -0,0 +1,19 @@ +QT += core + +REPC_REPLICA += TimeModel.rep +QT += remoteobjects + +QT -= gui + +TARGET = CppClient +CONFIG -= app_bundle + +TEMPLATE = app + +SOURCES += main.cpp + +OTHER_FILES += \ + TimeModel.rep + +target.path = $$[QT_INSTALL_EXAMPLES]/RemoteObjects/CppClient +INSTALLS += target diff --git a/examples/RemoteObjects/CppClient/TimeModel.rep b/examples/RemoteObjects/CppClient/TimeModel.rep new file mode 100644 index 0000000..fa7e612 --- /dev/null +++ b/examples/RemoteObjects/CppClient/TimeModel.rep @@ -0,0 +1,12 @@ +#include <QtCore> + +POD PresetInfo(int presetNumber, float frequency, QString stationName) +class MinuteTimer +{ + PROP(int hour=1); + PROP(int minute=51); + SIGNAL(timeChanged()); + SIGNAL(timeChanged2(QTime t)); + SIGNAL(sendCustom(PresetInfo info)); + SLOT(SetTimeZone(int zn)); +}; diff --git a/examples/RemoteObjects/CppClient/main.cpp b/examples/RemoteObjects/CppClient/main.cpp new file mode 100644 index 0000000..ee7ec65 --- /dev/null +++ b/examples/RemoteObjects/CppClient/main.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QCoreApplication> +#include <QTimer> +#include "rep_TimeModel_replica.h" + +class tester : public QObject +{ + Q_OBJECT +public: + tester() : QObject(Q_NULLPTR) + { + QRemoteObjectNode m_client = QRemoteObjectNode::createNodeConnectedToRegistry();; + ptr1.reset(m_client.acquire< MinuteTimerReplica >()); + ptr2.reset(m_client.acquire< MinuteTimerReplica >()); + ptr3.reset(m_client.acquire< MinuteTimerReplica >()); + QTimer::singleShot(0,this,SLOT(clear())); + QTimer::singleShot(1,this,SLOT(clear())); + QTimer::singleShot(10000,this,SLOT(clear())); + QTimer::singleShot(11000,this,SLOT(clear())); + } +public slots: + void clear() + { + static int i = 0; + if (i == 0) { + i++; + ptr1.reset(); + } else if (i == 1) { + i++; + ptr2.reset(); + } else if (i == 2) { + i++; + ptr3.reset(); + } else { + qApp->quit(); + } + } + +private: + QScopedPointer<MinuteTimerReplica> ptr1, ptr2, ptr3; +}; + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + tester t; + return a.exec(); +} + +#include "main.moc" diff --git a/examples/RemoteObjects/RemoteObjects.pro b/examples/RemoteObjects/RemoteObjects.pro new file mode 100644 index 0000000..da12899 --- /dev/null +++ b/examples/RemoteObjects/RemoteObjects.pro @@ -0,0 +1,11 @@ +TEMPLATE = subdirs +CONFIG += debug_and_release ordered +SUBDIRS = \ + server \ + CppClient + +qtHaveModule(quick) { + SUBDIRS += \ + plugins \ + ClientApp +} diff --git a/examples/RemoteObjects/TimeModel.rep b/examples/RemoteObjects/TimeModel.rep new file mode 100644 index 0000000..fa7e612 --- /dev/null +++ b/examples/RemoteObjects/TimeModel.rep @@ -0,0 +1,12 @@ +#include <QtCore> + +POD PresetInfo(int presetNumber, float frequency, QString stationName) +class MinuteTimer +{ + PROP(int hour=1); + PROP(int minute=51); + SIGNAL(timeChanged()); + SIGNAL(timeChanged2(QTime t)); + SIGNAL(sendCustom(PresetInfo info)); + SLOT(SetTimeZone(int zn)); +}; diff --git a/examples/RemoteObjects/plugins/imports/TimeExample/Clock.qml b/examples/RemoteObjects/plugins/imports/TimeExample/Clock.qml new file mode 100644 index 0000000..ca57cae --- /dev/null +++ b/examples/RemoteObjects/plugins/imports/TimeExample/Clock.qml @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 Digia Plc and its Subsidiary(-ies) 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$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Rectangle { + id: clock + width: 200; height: 200; color: "gray" + + property alias city: cityLabel.text + property variant hours + property variant minutes + property variant shift : 0 + + Image { id: background; source: "clock.png" } + + Image { + x: 92.5; y: 27 + source: "hour.png" + transform: Rotation { + id: hourRotation + origin.x: 7.5; origin.y: 73; + angle: (clock.hours * 30) + (clock.minutes * 0.5) + Behavior on angle { + SpringAnimation{ spring: 2; damping: 0.2; modulus: 360 } + } + } + } + + Image { + x: 93.5; y: 17 + source: "minute.png" + transform: Rotation { + id: minuteRotation + origin.x: 6.5; origin.y: 83; + angle: clock.minutes * 6 + Behavior on angle { + SpringAnimation{ spring: 2; damping: 0.2; modulus: 360 } + } + } + } + + Image { + anchors.centerIn: background; source: "center.png" + } + + Text { + id: cityLabel; font.bold: true; font.pixelSize: 14; y:200; color: "white" + anchors.horizontalCenter: parent.horizontalCenter + } +} diff --git a/examples/RemoteObjects/plugins/imports/TimeExample/center.png b/examples/RemoteObjects/plugins/imports/TimeExample/center.png Binary files differnew file mode 100644 index 0000000..7fbd802 --- /dev/null +++ b/examples/RemoteObjects/plugins/imports/TimeExample/center.png diff --git a/examples/RemoteObjects/plugins/imports/TimeExample/clock.png b/examples/RemoteObjects/plugins/imports/TimeExample/clock.png Binary files differnew file mode 100644 index 0000000..462edac --- /dev/null +++ b/examples/RemoteObjects/plugins/imports/TimeExample/clock.png diff --git a/examples/RemoteObjects/plugins/imports/TimeExample/hour.png b/examples/RemoteObjects/plugins/imports/TimeExample/hour.png Binary files differnew file mode 100644 index 0000000..f8061a1 --- /dev/null +++ b/examples/RemoteObjects/plugins/imports/TimeExample/hour.png diff --git a/examples/RemoteObjects/plugins/imports/TimeExample/minute.png b/examples/RemoteObjects/plugins/imports/TimeExample/minute.png Binary files differnew file mode 100644 index 0000000..1297ec7 --- /dev/null +++ b/examples/RemoteObjects/plugins/imports/TimeExample/minute.png diff --git a/examples/RemoteObjects/plugins/imports/TimeExample/qmldir b/examples/RemoteObjects/plugins/imports/TimeExample/qmldir new file mode 100644 index 0000000..252e662 --- /dev/null +++ b/examples/RemoteObjects/plugins/imports/TimeExample/qmldir @@ -0,0 +1,3 @@ +module TimeExample +Clock 1.0 Clock.qml +plugin qmlqtimeexampleplugin diff --git a/examples/RemoteObjects/plugins/plugin.cpp b/examples/RemoteObjects/plugins/plugin.cpp new file mode 100644 index 0000000..0415fc3 --- /dev/null +++ b/examples/RemoteObjects/plugins/plugin.cpp @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtQml/QQmlExtensionPlugin> +#include <QtQml/QQmlEngine> +#include <QtQml/qqml.h> +#include <QDebug> +#include <QDateTime> +#include <QBasicTimer> +#include <QCoreApplication> +#include <QRemoteObjectReplica> +#include <QRemoteObjectNode> +#include "rep_TimeModel_replica.h" + +// Implements a "TimeModel" class with hour and minute properties +// that change on-the-minute yet efficiently sleep the rest +// of the time. + +QRemoteObjectNode m_client; + +class TimeModel : public QObject +{ + Q_OBJECT + Q_PROPERTY(int hour READ hour NOTIFY timeChanged) + Q_PROPERTY(int minute READ minute NOTIFY timeChanged) + Q_PROPERTY(bool isValid READ isValid NOTIFY isValidChanged) + +public: + TimeModel(QObject *parent=0) : QObject(parent), d_ptr(Q_NULLPTR) + { + d_ptr.reset(m_client.acquire< MinuteTimerReplica >()); + connect(d_ptr.data(), SIGNAL(onHourChanged()), this, SIGNAL(timeChanged())); + connect(d_ptr.data(), SIGNAL(onMinuteChanged()), this, SIGNAL(timeChanged())); + connect(d_ptr.data(), SIGNAL(timeChanged()), this, SIGNAL(timeChanged())); + connect(d_ptr.data(), SIGNAL(timeChanged2(QTime)), this, SLOT(test(QTime))); + connect(d_ptr.data(), SIGNAL(sendCustom(PresetInfo)), this, SLOT(testCustom(PresetInfo))); + } + + ~TimeModel() + { + } + + int minute() const { return d_ptr->minute(); } + int hour() const { return d_ptr->hour(); } + bool isValid() const { return d_ptr->isReplicaValid(); } + +public slots: + //Test a signal with parameters + void test(QTime t) + { + qDebug()<<"Test"<<t; + d_ptr->SetTimeZone(t.minute()); + } + //Test a signal with a custom type + void testCustom(PresetInfo info) + { + qDebug()<<"testCustom"<<info.presetNumber()<<info.frequency()<<info.stationName(); + } + +signals: + void timeChanged(); + void timeChanged2(QTime t); + void sendCustom(PresetInfo info); + void isValidChanged(); + +private: + QScopedPointer<MinuteTimerReplica> d_ptr; +}; + +class QExampleQmlPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") + +public: + void initializeEngine(QQmlEngine *engine, const char *uri) + { + Q_UNUSED(engine); + Q_UNUSED(uri); + Q_ASSERT(uri == QLatin1String("TimeExample")); + engine->addImportPath(QStringLiteral("qrc:/qml")); + m_client = QRemoteObjectNode::createNodeConnectedToRegistry(); + } + void registerTypes(const char *uri) + { + Q_ASSERT(uri == QLatin1String("TimeExample")); + qmlRegisterType<TimeModel>(uri, 1, 0, "Time"); + } + +}; + +#include "plugin.moc" diff --git a/examples/RemoteObjects/plugins/plugins.pro b/examples/RemoteObjects/plugins/plugins.pro new file mode 100644 index 0000000..ce1518f --- /dev/null +++ b/examples/RemoteObjects/plugins/plugins.pro @@ -0,0 +1,28 @@ +QT += qml remoteobjects + +TEMPLATE = lib +CONFIG += plugin + +REPC_REPLICA += $$PWD/../TimeModel.rep + +DESTDIR = imports/TimeExample +TARGET = qmlqtimeexampleplugin + +SOURCES += plugin.cpp + +pluginfiles.files += \ + imports/TimeExample/qmldir \ + imports/TimeExample/center.png \ + imports/TimeExample/clock.png \ + imports/TimeExample/Clock.qml \ + imports/TimeExample/hour.png \ + imports/TimeExample/minute.png + +#qml.files = plugins.qml +#qml.path += $$[QT_HOST_PREFIX]/qml/plugins +target.path += $$[QT_HOST_PREFIX]/qml/TimeExample +pluginfiles.path += $$[QT_HOST_PREFIX]/qml/TimeExample + +INSTALLS += target pluginfiles + +contains(QT_CONFIG, c++11): CONFIG += c++11 diff --git a/examples/RemoteObjects/plugins/plugins.qml b/examples/RemoteObjects/plugins/plugins.qml new file mode 100644 index 0000000..87318a6 --- /dev/null +++ b/examples/RemoteObjects/plugins/plugins.qml @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + width: 200 + height: 400 + property int counter: 0; + MouseArea { + anchors.fill: parent + onClicked: + { + counter = (counter + 1) % 3; + console.log(counter); + pageLoader.source = "plugins"+counter+".qml" + } + } + Loader { + id: pageLoader + source: "plugins0.qml" + } +} +//![0] diff --git a/examples/RemoteObjects/plugins/plugins0.qml b/examples/RemoteObjects/plugins/plugins0.qml new file mode 100644 index 0000000..958ae9f --- /dev/null +++ b/examples/RemoteObjects/plugins/plugins0.qml @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 Digia Plc and its Subsidiary(-ies) 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$ +** +****************************************************************************/ +//![0] +import QtQuick 2.0 + +Rectangle { + width: 200 + height: 400 + color: "blue" +} +//![0] diff --git a/examples/RemoteObjects/plugins/plugins1.qml b/examples/RemoteObjects/plugins/plugins1.qml new file mode 100644 index 0000000..c9a9237 --- /dev/null +++ b/examples/RemoteObjects/plugins/plugins1.qml @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 Digia Plc and its Subsidiary(-ies) 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$ +** +****************************************************************************/ +//![0] +import QtQuick 2.0 +import TimeExample 1.0 // import types from the plugin + +Rectangle { + width: 200 + height: 400 + color: "blue" + Clock { // this class is defined in QML (imports/TimeExample/Clock.qml) + id: clock1 + anchors.top: parent.top + Time { // this class is defined in C++ (plugin.cpp) + id: time + } + + hours: time.hour + minutes: time.minute + + } +} +//![0] diff --git a/examples/RemoteObjects/plugins/plugins2.qml b/examples/RemoteObjects/plugins/plugins2.qml new file mode 100644 index 0000000..55acb8a --- /dev/null +++ b/examples/RemoteObjects/plugins/plugins2.qml @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 Digia Plc and its Subsidiary(-ies) 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$ +** +****************************************************************************/ +//![0] +import QtQuick 2.0 +import TimeExample 1.0 // import types from the plugin + +Rectangle { + width: 200 + height: 400 + Clock { // this class is defined in QML (imports/TimeExample/Clock.qml) + id: clock1 + anchors.top: parent.top + Time { // this class is defined in C++ (plugin.cpp) + id: time + } + + hours: time.hour + minutes: time.minute + + } + Clock { // this class is defined in QML (imports/TimeExample/Clock.qml) + id: clock2 + anchors.top: clock1.bottom + Time { // this class is defined in C++ (plugin.cpp) + id: time2 + } + + hours: time2.hour + minutes: time2.minute + + } + +} +//![0] diff --git a/examples/RemoteObjects/server/TimeModel.cpp b/examples/RemoteObjects/server/TimeModel.cpp new file mode 100644 index 0000000..1654a1a --- /dev/null +++ b/examples/RemoteObjects/server/TimeModel.cpp @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "TimeModel.h" + +MinuteTimer::MinuteTimer(QObject *parent) : MinuteTimerSource(parent) +{ + time = QTime::currentTime(); + setHour(time.hour()); + setMinute(time.minute()); + timer.start(60000-time.second()*1000, this); +} +MinuteTimer::~MinuteTimer() +{ + timer.stop(); +} +void MinuteTimer::timerEvent(QTimerEvent *) +{ + QTime now = QTime::currentTime(); + if (now.second() == 59 && now.minute() == time.minute() && now.hour() == time.hour()) { + // just missed time tick over, force it, wait extra 0.5 seconds + time.addSecs(60); + timer.start(60500, this); + } else { + time = now; + timer.start(60000-time.second()*1000, this); + } + qDebug()<<"Time"<<time; + setHour(time.hour()); + setMinute(time.minute()); + emit timeChanged(); + emit timeChanged2(time); + static PresetInfo bla(3, 93.9f, "Best Station"); + emit sendCustom(bla); +} +void MinuteTimer::SetTimeZone(int zn) +{ + qDebug()<<"SetTimeZone"<<zn; + if (zn != zone) + { + zone = zn; + } +} diff --git a/examples/RemoteObjects/server/TimeModel.h b/examples/RemoteObjects/server/TimeModel.h new file mode 100644 index 0000000..67c87c0 --- /dev/null +++ b/examples/RemoteObjects/server/TimeModel.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore> +#include "rep_TimeModel_source.h" + +class MinuteTimer : public MinuteTimerSource +{ + Q_OBJECT +public: + MinuteTimer(QObject *parent=Q_NULLPTR); + virtual ~MinuteTimer(); + +public slots: + virtual void SetTimeZone(int zn); + +protected: + void timerEvent(QTimerEvent *); + +private: + QTime time; + QBasicTimer timer; + int zone; +}; diff --git a/examples/RemoteObjects/server/main.cpp b/examples/RemoteObjects/server/main.cpp new file mode 100644 index 0000000..15f76a2 --- /dev/null +++ b/examples/RemoteObjects/server/main.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "TimeModel.h" + +#include <QCoreApplication> +/* +* http://stackoverflow.com/questions/7404163/windows-handling-ctrlc-in-different-thread +*/ + +void SigIntHandler() +{ + qDebug()<<"Ctrl-C received. Quitting."; + qApp->quit(); +} + +#if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_QNX) + #include <signal.h> + + void unix_handler(int s) + { + if (s==SIGINT) + SigIntHandler(); + } + +#elif defined(Q_OS_WIN32) + #include <windows.h> + + BOOL WINAPI WinHandler(DWORD CEvent) + { + switch (CEvent) + { + case CTRL_C_EVENT: + SigIntHandler(); + break; + } + return TRUE; + } +#endif + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + + #if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_QNX) + signal(SIGINT, &unix_handler); + #elif defined(Q_OS_WIN32) + SetConsoleCtrlHandler((PHANDLER_ROUTINE)WinHandler, TRUE); + #endif + QRemoteObjectNode node = QRemoteObjectNode::createRegistryHostNode(); + QRemoteObjectNode node2 = QRemoteObjectNode::createHostNodeConnectedToRegistry(); + MinuteTimer timer; + node2.enableRemoting(&timer); + + Q_UNUSED(timer); + return app.exec(); +} diff --git a/examples/RemoteObjects/server/server.pro b/examples/RemoteObjects/server/server.pro new file mode 100644 index 0000000..9d624fd --- /dev/null +++ b/examples/RemoteObjects/server/server.pro @@ -0,0 +1,13 @@ +CONFIG += console + + +REPC_SOURCE += ../TimeModel.rep +QT = remoteobjects core + +SOURCES += TimeModel.cpp main.cpp +HEADERS += TimeModel.h + +contains(QT_CONFIG, c++11): CONFIG += c++11 + +target.path = $$[QT_INSTALL_EXAMPLES]/RemoteObjects/Server +INSTALLS += target diff --git a/examples/examples.pro b/examples/examples.pro new file mode 100644 index 0000000..698f31d --- /dev/null +++ b/examples/examples.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = \ + RemoteObjects diff --git a/mkspecs/features/features.pro b/mkspecs/features/features.pro new file mode 100644 index 0000000..8fc706b --- /dev/null +++ b/mkspecs/features/features.pro @@ -0,0 +1,4 @@ +prf.files = remoteobjects_repc.prf repcclient.pri repcserver.pri repccommon.pri +prf.path = $$[QT_INSTALL_DATA]/mkspecs/features +INSTALLS += prf +TEMPLATE = subdirs diff --git a/mkspecs/features/remoteobjects_repc.prf b/mkspecs/features/remoteobjects_repc.prf new file mode 100644 index 0000000..183cc16 --- /dev/null +++ b/mkspecs/features/remoteobjects_repc.prf @@ -0,0 +1,3 @@ +include(repcclient.pri) +include(repcserver.pri) + diff --git a/mkspecs/features/repcclient.pri b/mkspecs/features/repcclient.pri new file mode 100644 index 0000000..2a95b2e --- /dev/null +++ b/mkspecs/features/repcclient.pri @@ -0,0 +1,4 @@ +repc_type = replica +repc_option = -r + +include(repccommon.pri) diff --git a/mkspecs/features/repccommon.pri b/mkspecs/features/repccommon.pri new file mode 100644 index 0000000..c176822 --- /dev/null +++ b/mkspecs/features/repccommon.pri @@ -0,0 +1,53 @@ +qtPrepareTool(QMAKE_REPC, repc) + +REPC_INCLUDEPATHES = $$QT.remoteobjects.includes +for (path, REPC_INCLUDEPATHES) { + REPC_INCLUDEPATH += -I $$path +} + +isEmpty(QMAKE_MOD_REPC):QMAKE_MOD_REPC = rep_ + +repc_TYPE = $$upper($$repc_type) + +load(moc) + +groups = +for(entry, REPC_$$repc_TYPE) { + files = $$eval($${entry}.files) + isEmpty(files) { + files = $$entry + group = repc_$${repc_type} + } else { + group = $${entry}_repc_$${repc_type} + } + groups *= $$group + + input_list = $$upper($$group)_LIST + for(subent, $$list($$unique(files))) { + + !contains(subent, .*\\w\\.rep$) { + warning("Invalid REPC $${repc_type}: '$$subent', please use '*.rep' instead.") + next() + } + $$input_list += $$subent + } +} + +for(group, groups) { + GROUP = $$upper($$group) + input_list = $${GROUP}_LIST + + $${group}_header.output = $$QMAKE_MOD_REPC${QMAKE_FILE_BASE}_$${repc_type}.h + $${group}_header.commands = $$QMAKE_REPC $$repc_option -i ${QMAKE_FILE_NAME} -o ${QMAKE_FILE_OUT} + $${group}_header.depends = ${QMAKE_FILE_NAME} $$QT_TOOL.repc.binary + $${group}_header.variable_out = $${GROUP}_HEADERS + $${group}_header.input = $$input_list + + $${group}_moc.commands = $$moc_header.commands $$REPC_INCLUDEPATH + $${group}_moc.output = $$moc_header.output + $${group}_moc.input = $${GROUP}_HEADERS + $${group}_moc.variable_out = GENERATED_SOURCES + $${group}_moc.name = $$moc_header.name + + QMAKE_EXTRA_COMPILERS += $${group}_header $${group}_moc +} diff --git a/mkspecs/features/repcserver.pri b/mkspecs/features/repcserver.pri new file mode 100644 index 0000000..f5d8dfd --- /dev/null +++ b/mkspecs/features/repcserver.pri @@ -0,0 +1,4 @@ +repc_type = source +repc_option = -s + +include(repccommon.pri) diff --git a/mkspecs/mkspecs.pro b/mkspecs/mkspecs.pro new file mode 100644 index 0000000..95262a0 --- /dev/null +++ b/mkspecs/mkspecs.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +SUBDIRS += features diff --git a/qtremoteobjects.pro b/qtremoteobjects.pro new file mode 100644 index 0000000..c2e2b6b --- /dev/null +++ b/qtremoteobjects.pro @@ -0,0 +1,4 @@ +CONFIG += examples_need_tools tests_need_tools +load(qt_parts) + +SUBDIRS += mkspecs diff --git a/src/remoteobjects/doc/images/README b/src/remoteobjects/doc/images/README new file mode 100644 index 0000000..aec41a2 --- /dev/null +++ b/src/remoteobjects/doc/images/README @@ -0,0 +1 @@ +Put all documentation images into this folder. diff --git a/src/remoteobjects/doc/qtremoteobjects.qdocconf b/src/remoteobjects/doc/qtremoteobjects.qdocconf new file mode 100644 index 0000000..69392af --- /dev/null +++ b/src/remoteobjects/doc/qtremoteobjects.qdocconf @@ -0,0 +1,43 @@ +include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) + +project = QtRemoteObjects +description = Qt RemoteObjects Reference Documentation +version = $QT_VERSION + +qhp.projects = QtRemoteObjects + +qhp.QtRemoteObjects.file = qtremoteobjects.qhp +qhp.QtRemoteObjects.namespace = org.qt-project.qtremoteobjects.$QT_VERSION_TAG +qhp.QtRemoteObjects.virtualFolder = qtremoteobjects +qhp.QtRemoteObjects.indexTitle = Qt RemoteObjects +qhp.QtRemoteObjects.indexRoot = + +qhp.QtRemoteObjects.filterAttributes = qtremoteobjects $QT_VERSION qtrefdoc +qhp.QtRemoteObjects.customFilters.Qt.name = QtRemoteObjects $QT_VERSION +qhp.QtRemoteObjects.customFilters.Qt.filterAttributes = qtremoteobjects $QT_VERSION +qhp.QtRemoteObjects.subprojects = classes +qhp.QtRemoteObjects.subprojects.classes.title = C++ Classes +qhp.QtRemoteObjects.subprojects.classes.indexTitle = Qt RemoteObjects C++ Classes +qhp.QtRemoteObjects.subprojects.classes.selectors = class fake:headerfile +qhp.QtRemoteObjects.subprojects.classes.sortPages = true + +depends += qtcore \ + qtdoc + +tagfile = ../../../doc/qtremoteobjects/qtremoteobjects.tagsi + +headerdirs += .. \ + ../../remoteobjects + +sourcedirs += .. \ + ../../remoteobjects + +exampledirs += ../../../examples/RemoteObjects \ + snippets/ + +examplesinstallpath = RemoteObjects + +imagedirs += images + +navigation.landingpage = "Qt RemoteObjects" +navigation.cppclassespage = "Qt RemoteObjects C++ Classes" diff --git a/src/remoteobjects/doc/snipplets/README b/src/remoteobjects/doc/snipplets/README new file mode 100644 index 0000000..72ad410 --- /dev/null +++ b/src/remoteobjects/doc/snipplets/README @@ -0,0 +1 @@ +Put all snipplets into this folder. diff --git a/src/remoteobjects/doc/src/qtremoteobjects.qdoc b/src/remoteobjects/doc/src/qtremoteobjects.qdoc new file mode 100644 index 0000000..231ebd2 --- /dev/null +++ b/src/remoteobjects/doc/src/qtremoteobjects.qdoc @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \module QtRemoteObjects + \title Qt RemoteObjects C++ Classes + \ingroup modules + \qtvariable remote objects + + \brief The Qt RemoteObjects module provides functionality for remoting QObjects. + +*/ diff --git a/src/remoteobjects/qconnectionabstractfactory_p.h b/src/remoteobjects/qconnectionabstractfactory_p.h new file mode 100644 index 0000000..b681695 --- /dev/null +++ b/src/remoteobjects/qconnectionabstractfactory_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONNECTIONABSTRACTFACTORY_P_H +#define QCONNECTIONABSTRACTFACTORY_P_H + +#include <QHash> + +QT_BEGIN_NAMESPACE + +class QObject; + +template <class OutputType, class IndexType = QString> +class QConnectionAbstractFactory +{ + Q_DISABLE_COPY(QConnectionAbstractFactory) + +public: + QConnectionAbstractFactory() {} + + template<class T> + void registerProduct(const IndexType &scheme) + { + m_mapping[scheme] = &createInstance<T>; + } + + OutputType *create(const IndexType &type, QObject *parent = Q_NULLPTR) const + { + typename QHash<IndexType, FactoryFunction>::const_iterator res = m_mapping.find(type); + if (res != m_mapping.end()) + return (*res)(parent); + else + return Q_NULLPTR; + } + +private: + typedef OutputType* (*FactoryFunction)(QObject*); + QHash<IndexType, FactoryFunction> m_mapping; + + template<class Type> + static OutputType* createInstance(QObject *parent) + { + return new Type(parent); + } +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qconnectionabstractserver.cpp b/src/remoteobjects/qconnectionabstractserver.cpp new file mode 100644 index 0000000..672bd2e --- /dev/null +++ b/src/remoteobjects/qconnectionabstractserver.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qconnectionabstractserver_p.h" +#include "qtremoteobjectglobal.h" + +QT_BEGIN_NAMESPACE + +ServerIoDevice::ServerIoDevice(QObject *parent) + : QObject(parent), m_isClosing(false), m_curReadSize(0), m_packet(Q_NULLPTR) +{ +} + +ServerIoDevice::~ServerIoDevice() +{ + if (m_packet) + delete m_packet; +} + +bool ServerIoDevice::read() +{ + qCDebug(QT_REMOTEOBJECT) << "ServerIODevice::read()" << m_curReadSize << bytesAvailable(); + + QDataStream in(connection()); + if (m_curReadSize == 0) { + if (bytesAvailable() < static_cast<int>(sizeof(quint32))) + return false; + + in >> m_curReadSize; + } + + qCDebug(QT_REMOTEOBJECT) << "ServerIODevice::read()-looking for map" << m_curReadSize << bytesAvailable(); + + if (bytesAvailable() < m_curReadSize) + return false; + + m_curReadSize = 0; + if (m_packet) + delete m_packet; + m_packet = QRemoteObjectPackets::QRemoteObjectPacket::fromDataStream(in); + return m_packet && m_packet->id != QRemoteObjectPackets::QRemoteObjectPacket::Invalid; +} + +void ServerIoDevice::close() +{ + m_isClosing = true; + doClose(); +} + +void ServerIoDevice::write(const QByteArray &data) +{ + if (connection()->isOpen() && !m_isClosing) + connection()->write(data); +} + +qint64 ServerIoDevice::bytesAvailable() +{ + return connection()->bytesAvailable(); +} + +QRemoteObjectPackets::QRemoteObjectPacket *ServerIoDevice::packet() const +{ + return m_packet; +} + + +QConnectionAbstractServer::QConnectionAbstractServer(QObject *parent) + : QObject(parent) +{ +} + +QConnectionAbstractServer::~QConnectionAbstractServer() +{ +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qconnectionabstractserver_p.h b/src/remoteobjects/qconnectionabstractserver_p.h new file mode 100644 index 0000000..4d55f56 --- /dev/null +++ b/src/remoteobjects/qconnectionabstractserver_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONNECTIONABSTRACTSERVER_P_H +#define QCONNECTIONABSTRACTSERVER_P_H + +#include <QAbstractSocket> +#include <QDataStream> +#include <QIODevice> +#include <QLocalSocket> +#include <QObject> +#include <QTcpSocket> +#include <QVariant> +#include "qtremoteobjectglobal.h" + +QT_BEGIN_NAMESPACE + +//The Qt servers create QIODevice derived classes from handleConnection. +//The problem is that they behave differently, so this class adds some +//consistency. +class ServerIoDevice : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(ServerIoDevice) + +public: + explicit ServerIoDevice(QObject *parent = Q_NULLPTR); + virtual ~ServerIoDevice(); + + bool read(); + + virtual void write(const QByteArray &data); + void close(); + virtual qint64 bytesAvailable(); + QRemoteObjectPackets::QRemoteObjectPacket *packet() const; + virtual QIODevice *connection() const = 0; + +Q_SIGNALS: + void disconnected(); + void readyRead(); + +protected: + virtual void doClose() = 0; + +private: + bool m_isClosing; + quint32 m_curReadSize; + QRemoteObjectPackets::QRemoteObjectPacket *m_packet; +}; + + +class QConnectionAbstractServer : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(QConnectionAbstractServer) + +public: + explicit QConnectionAbstractServer(QObject *parent = Q_NULLPTR); + virtual ~QConnectionAbstractServer(); + + virtual bool hasPendingConnections() const = 0; + virtual ServerIoDevice* nextPendingConnection() = 0; + virtual QUrl address() const = 0; + virtual bool listen(const QUrl &address) = 0; + virtual QAbstractSocket::SocketError serverError() const = 0; + virtual void close() = 0; + +Q_SIGNALS: + void newConnection(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qconnectionclientfactory.cpp b/src/remoteobjects/qconnectionclientfactory.cpp new file mode 100644 index 0000000..23e1527 --- /dev/null +++ b/src/remoteobjects/qconnectionclientfactory.cpp @@ -0,0 +1,279 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qconnectionclientfactory_p.h" +#include "qtremoteobjectglobal.h" + +#include <QDataStream> +#include <QHostAddress> +#include <QHostInfo> + +QT_BEGIN_NAMESPACE + +ClientIoDevice::ClientIoDevice(QObject *parent) + : QObject(parent), m_isClosing(false), m_curReadSize(0), m_packet(Q_NULLPTR) +{ +} + +ClientIoDevice::~ClientIoDevice() +{ + if (m_packet) + delete m_packet; +} + +void ClientIoDevice::close() +{ + m_isClosing = true; + doClose(); +} + +bool ClientIoDevice::read() +{ + qCDebug(QT_REMOTEOBJECT) << "ClientIODevice::read()" << m_curReadSize << bytesAvailable(); + + QDataStream in(connection()); + if (m_curReadSize == 0) { + if (bytesAvailable() < static_cast<int>(sizeof(quint32))) + return false; + + in >> m_curReadSize; + } + + qCDebug(QT_REMOTEOBJECT) << "ClientIODevice::read()-looking for map" << m_curReadSize << bytesAvailable(); + + if (bytesAvailable() < m_curReadSize) + return false; + + m_curReadSize = 0; + if (m_packet) + delete m_packet; + m_packet = QRemoteObjectPackets::QRemoteObjectPacket::fromDataStream(in); + return m_packet && m_packet->id != QRemoteObjectPackets::QRemoteObjectPacket::Invalid; +} + +void ClientIoDevice::write(const QByteArray &data) +{ + connection()->write(data); +} + +qint64 ClientIoDevice::bytesAvailable() +{ + return connection()->bytesAvailable(); +} + +QRemoteObjectPackets::QRemoteObjectPacket *ClientIoDevice::packet() const +{ + return m_packet; +} + +QUrl ClientIoDevice::url() const +{ + return m_url; +} + +void ClientIoDevice::addSource(const QString &name) +{ + m_remoteObjects.insert(name); +} + +void ClientIoDevice::removeSource(const QString &name) +{ + m_remoteObjects.remove(name); +} + +QSet<QString> ClientIoDevice::remoteObjects() const +{ + return m_remoteObjects; +} + + +LocalClientIo::LocalClientIo(QObject *parent) + : ClientIoDevice(parent) +{ + connect(&m_socket, SIGNAL(readyRead()), this, SIGNAL(readyRead())); + connect(&m_socket, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(onError(QLocalSocket::LocalSocketError))); + connect(&m_socket, SIGNAL(stateChanged(QLocalSocket::LocalSocketState)), this, SLOT(onStateChanged(QLocalSocket::LocalSocketState))); +} + +LocalClientIo::~LocalClientIo() +{ + close(); +} + +QIODevice *LocalClientIo::connection() +{ + return &m_socket; +} + +void LocalClientIo::doClose() +{ + if (m_socket.isOpen()) { + connect(&m_socket, SIGNAL(disconnected()), this, SLOT(deleteLater())); + m_socket.disconnectFromServer(); + } else { + this->deleteLater(); + } +} + +void LocalClientIo::connectToServer() +{ + if (!isOpen()) + m_socket.connectToServer(url().path()); +} + +bool LocalClientIo::isOpen() +{ + return !isClosing() && m_socket.isOpen(); +} + +void LocalClientIo::onError(QLocalSocket::LocalSocketError error) +{ + qCDebug(QT_REMOTEOBJECT) << "onError" << error; + + switch (error) { + case QLocalSocket::ServerNotFoundError: //Host not there, wait and try again + emit shouldReconnect(this); + break; + case QLocalSocket::ConnectionError: + case QLocalSocket::ConnectionRefusedError: + //... TODO error reporting + break; + default: + break; + } +} + +void LocalClientIo::onStateChanged(QLocalSocket::LocalSocketState state) +{ + if (state == QLocalSocket::ClosingState && !isClosing()) { + m_socket.abort(); + emit shouldReconnect(this); + } +} + + +TcpClientIo::TcpClientIo(QObject *parent) + : ClientIoDevice(parent) +{ + connect(&m_socket, SIGNAL(readyRead()), this, SIGNAL(readyRead())); + connect(&m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError))); + connect(&m_socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onStateChanged(QAbstractSocket::SocketState))); +} + +TcpClientIo::~TcpClientIo() +{ + close(); +} + +QIODevice *TcpClientIo::connection() +{ + return &m_socket; +} + +void TcpClientIo::doClose() +{ + if (m_socket.isOpen()) { + connect(&m_socket, SIGNAL(disconnected()), this, SLOT(deleteLater())); + m_socket.disconnectFromHost(); + } else { + this->deleteLater(); + } +} + +void TcpClientIo::connectToServer() +{ + if (isOpen()) + return; + QHostAddress address(url().host()); + if (address.isNull()) { + const QList<QHostAddress> addresses = QHostInfo::fromName(url().host()).addresses(); + Q_ASSERT_X(addresses.size() >= 1, Q_FUNC_INFO, url().toString().toLatin1().data()); + address = addresses.first(); + } + + m_socket.connectToHost(address, url().port()); +} + +bool TcpClientIo::isOpen() +{ + return (!isClosing() && m_socket.isOpen()); +} + +void TcpClientIo::onError(QAbstractSocket::SocketError error) +{ + qCDebug(QT_REMOTEOBJECT) << "onError" << error; + + switch (error) { + case QAbstractSocket::HostNotFoundError: //Host not there, wait and try again + emit shouldReconnect(this); + break; + case QAbstractSocket::AddressInUseError: + case QAbstractSocket::ConnectionRefusedError: + //... TODO error reporting + break; + default: + break; + } +} + +void TcpClientIo::onStateChanged(QAbstractSocket::SocketState state) +{ + if (state == QAbstractSocket::ClosingState && !isClosing()) { + m_socket.abort(); + emit shouldReconnect(this); + } +} + + +QConnectionClientFactory::QConnectionClientFactory() +{ + registerProduct<LocalClientIo>(QRemoteObjectStringLiterals::local()); + registerProduct<TcpClientIo>(QRemoteObjectStringLiterals::tcp()); +} + +ClientIoDevice *QConnectionClientFactory::createDevice(const QUrl &url, QObject *parent) +{ + ClientIoDevice *res = QConnectionAbstractFactory::create(url.scheme(), parent); + res->m_url = url; + return res; +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qconnectionclientfactory_p.h b/src/remoteobjects/qconnectionclientfactory_p.h new file mode 100644 index 0000000..5fa0cce --- /dev/null +++ b/src/remoteobjects/qconnectionclientfactory_p.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONNECTIONCLIENTFACTORY_P_H +#define QCONNECTIONCLIENTFACTORY_P_H + +#include "qconnectionabstractfactory_p.h" +#include "qtremoteobjectglobal.h" + +#include <QLocalSocket> +#include <QObject> +#include <QTcpSocket> +#include <QUrl> +#include <QVariant> + +QT_BEGIN_NAMESPACE + +class ClientIoDevice : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(ClientIoDevice) + +public: + explicit ClientIoDevice(QObject *parent = Q_NULLPTR); + virtual ~ClientIoDevice(); + + bool read(); + virtual void write(const QByteArray &data); + void close(); + virtual void connectToServer() = 0; + virtual qint64 bytesAvailable(); + + QRemoteObjectPackets::QRemoteObjectPacket *packet() const; + QUrl url() const; + void addSource(const QString &); + void removeSource(const QString &); + QSet<QString> remoteObjects() const; + + virtual bool isOpen() = 0; + virtual QIODevice *connection() = 0; + +Q_SIGNALS: + void disconnected(); + void readyRead(); + void shouldReconnect(ClientIoDevice*); +protected: + virtual void doClose() = 0; + inline bool isClosing(); + +private: + bool m_isClosing; + QUrl m_url; + +private: + friend class QConnectionClientFactory; + + quint32 m_curReadSize; + QRemoteObjectPackets::QRemoteObjectPacket* m_packet; + QSet<QString> m_remoteObjects; +}; + +bool ClientIoDevice::isClosing() +{ + return m_isClosing; +} + +class LocalClientIo : public ClientIoDevice +{ + Q_OBJECT + +public: + explicit LocalClientIo(QObject *parent = Q_NULLPTR); + ~LocalClientIo(); + + QIODevice *connection() Q_DECL_OVERRIDE; + void connectToServer() Q_DECL_OVERRIDE; + bool isOpen() Q_DECL_OVERRIDE; + +public Q_SLOTS: + void onError(QLocalSocket::LocalSocketError error); + void onStateChanged(QLocalSocket::LocalSocketState state); + +protected: + void doClose() Q_DECL_OVERRIDE; +private: + QLocalSocket m_socket; +}; + + +class TcpClientIo : public ClientIoDevice +{ + Q_OBJECT + +public: + explicit TcpClientIo(QObject *parent = Q_NULLPTR); + ~TcpClientIo(); + + QIODevice *connection() Q_DECL_OVERRIDE; + void connectToServer() Q_DECL_OVERRIDE; + bool isOpen() Q_DECL_OVERRIDE; + +public Q_SLOTS: + void onError(QAbstractSocket::SocketError error); + void onStateChanged(QAbstractSocket::SocketState state); + +protected: + void doClose() Q_DECL_OVERRIDE; + +private: + QTcpSocket m_socket; +}; + +class QConnectionClientFactory : public QConnectionAbstractFactory<ClientIoDevice> +{ + Q_DISABLE_COPY(QConnectionClientFactory) + +public: + QConnectionClientFactory(); + + ClientIoDevice *createDevice(const QUrl &url, QObject *parent = Q_NULLPTR); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qconnectionserverfactory.cpp b/src/remoteobjects/qconnectionserverfactory.cpp new file mode 100644 index 0000000..f211ee7 --- /dev/null +++ b/src/remoteobjects/qconnectionserverfactory.cpp @@ -0,0 +1,217 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qconnectionserverfactory_p.h" + +#include <qcompilerdetection.h> +#include <QHostInfo> +#include <QIODevice> +#include <QLocalServer> +#include <QTcpServer> +#include <QtGlobal> + +#ifdef Q_OS_LINUX +#include <QFile> +#include <QDir> +#endif + +QT_BEGIN_NAMESPACE + +LocalServerIo::LocalServerIo(QLocalSocket *conn, QObject *parent) + : ServerIoDevice(parent), m_connection(conn) +{ + m_connection->setParent(this); + connect(conn, SIGNAL(readyRead()), this, SIGNAL(readyRead())); + connect(conn, SIGNAL(disconnected()), this, SIGNAL(disconnected())); +} + +QIODevice *LocalServerIo::connection() const +{ + return m_connection; +} + +void LocalServerIo::doClose() +{ + m_connection->disconnectFromServer(); +} + + +TcpServerIo::TcpServerIo(QTcpSocket *conn, QObject *parent) + : ServerIoDevice(parent), m_connection(conn) +{ + m_connection->setParent(this); + connect(conn, SIGNAL(readyRead()), this, SIGNAL(readyRead())); + connect(conn, SIGNAL(disconnected()), this, SIGNAL(disconnected())); +} + +QIODevice *TcpServerIo::connection() const +{ + return m_connection; +} + +void TcpServerIo::doClose() +{ + m_connection->disconnectFromHost(); +} + + +LocalServerImpl::LocalServerImpl(QObject *parent) + : QConnectionAbstractServer(parent) +{ + connect(&m_server, SIGNAL(newConnection()), this, SIGNAL(newConnection())); +} + +LocalServerImpl::~LocalServerImpl() +{ + m_server.close(); +} + +ServerIoDevice *LocalServerImpl::nextPendingConnection() +{ + if (!m_server.isListening()) + return Q_NULLPTR; + + return new LocalServerIo(m_server.nextPendingConnection(), this); +} + +bool LocalServerImpl::hasPendingConnections() const +{ + return m_server.hasPendingConnections(); +} + +QUrl LocalServerImpl::address() const +{ + QUrl result; + result.setPath(m_server.serverName()); + result.setScheme(QRemoteObjectStringLiterals::local()); + + return result; +} + +bool LocalServerImpl::listen(const QUrl &address) +{ +#ifdef Q_OS_LINUX + QFile socketFile(QDir::tempPath() + QDir::separator() + address.path()); + socketFile.remove(); +#endif + return m_server.listen(address.path()); +} + +QAbstractSocket::SocketError LocalServerImpl::serverError() const +{ + return m_server.serverError(); +} + +void LocalServerImpl::close() +{ + close(); +} + + +TcpServerImpl::TcpServerImpl(QObject *parent) + : QConnectionAbstractServer(parent) +{ + connect(&m_server, SIGNAL(newConnection()), this, SIGNAL(newConnection())); +} + +TcpServerImpl::~TcpServerImpl() +{ + close(); +} + +ServerIoDevice *TcpServerImpl::nextPendingConnection() +{ + if (!m_server.isListening()) + return Q_NULLPTR; + + return new TcpServerIo(m_server.nextPendingConnection()); +} + +bool TcpServerImpl::hasPendingConnections() const +{ + return m_server.hasPendingConnections(); +} + +QUrl TcpServerImpl::address() const +{ + return m_originalUrl; +} + +bool TcpServerImpl::listen(const QUrl &address) +{ + QHostAddress host(address.host()); + if (host.isNull()) { + const QList<QHostAddress> addresses = QHostInfo::fromName(address.host()).addresses();; + Q_ASSERT(addresses.size() >= 1); + host = addresses.first(); + m_originalUrl = address; + } + + return m_server.listen(host, address.port()); +} + +QAbstractSocket::SocketError TcpServerImpl::serverError() const +{ + return m_server.serverError(); +} + +void TcpServerImpl::close() +{ + m_server.close(); +} + +QConnectionServerFactory::QConnectionServerFactory() +{ + registerProduct<LocalServerImpl>(QRemoteObjectStringLiterals::local()); + registerProduct<TcpServerImpl>(QRemoteObjectStringLiterals::tcp()); +} + +QConnectionAbstractServer *QConnectionServerFactory::createServer(const QUrl &url, QObject *parent) +{ + return create(url, parent); +} + +QConnectionAbstractServer *QConnectionServerFactory::create(const QUrl &url, QObject *parent) +{ + return QConnectionAbstractFactory::create(url.scheme(), parent); +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qconnectionserverfactory_p.h b/src/remoteobjects/qconnectionserverfactory_p.h new file mode 100644 index 0000000..4d4638a --- /dev/null +++ b/src/remoteobjects/qconnectionserverfactory_p.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONNECTIONSERVERFACTORY_P_H +#define QCONNECTIONSERVERFACTORY_P_H + +#include "qconnectionabstractfactory_p.h" +#include "qconnectionabstractserver_p.h" + +#include <QLocalServer> +#include <QTcpServer> +#include <QUrl> + +QT_BEGIN_NAMESPACE + +class LocalServerIo : public ServerIoDevice +{ +public: + explicit LocalServerIo(QLocalSocket *conn, QObject *parent = Q_NULLPTR); + + QIODevice *connection() const Q_DECL_OVERRIDE; +protected: + void doClose() Q_DECL_OVERRIDE; + +private: + QLocalSocket *m_connection; +}; + + +class TcpServerIo : public ServerIoDevice +{ +public: + explicit TcpServerIo(QTcpSocket *conn, QObject *parent = Q_NULLPTR); + + QIODevice *connection() const Q_DECL_OVERRIDE; +protected: + void doClose() Q_DECL_OVERRIDE; + +private: + QTcpSocket *m_connection; +}; + + +class LocalServerImpl : public QConnectionAbstractServer +{ + Q_OBJECT + Q_DISABLE_COPY(LocalServerImpl) + +public: + explicit LocalServerImpl(QObject *parent); + ~LocalServerImpl(); + + bool hasPendingConnections() const Q_DECL_OVERRIDE; + ServerIoDevice *nextPendingConnection() Q_DECL_OVERRIDE; + QUrl address() const Q_DECL_OVERRIDE; + bool listen(const QUrl &address) Q_DECL_OVERRIDE; + QAbstractSocket::SocketError serverError() const Q_DECL_OVERRIDE; + void close() Q_DECL_OVERRIDE; + +private: + QLocalServer m_server; +}; + + +class TcpServerImpl : public QConnectionAbstractServer +{ + Q_OBJECT + Q_DISABLE_COPY(TcpServerImpl) + +public: + explicit TcpServerImpl(QObject *parent); + ~TcpServerImpl(); + + bool hasPendingConnections() const Q_DECL_OVERRIDE; + ServerIoDevice *nextPendingConnection() Q_DECL_OVERRIDE; + QUrl address() const Q_DECL_OVERRIDE; + bool listen(const QUrl &address) Q_DECL_OVERRIDE; + QAbstractSocket::SocketError serverError() const Q_DECL_OVERRIDE; + void close() Q_DECL_OVERRIDE; + +private: + QTcpServer m_server; + QUrl m_originalUrl; // necessary because of a QHostAddress bug +}; + + +class QConnectionServerFactory : public QConnectionAbstractFactory<QConnectionAbstractServer> +{ + Q_DISABLE_COPY(QConnectionServerFactory) + +public: + QConnectionServerFactory(); + QConnectionAbstractServer *createServer(const QUrl &url, QObject *parent = Q_NULLPTR); + static void registerScheme(const QString &scheme); + +private: + QConnectionAbstractServer *create(const QUrl &url, QObject *parent); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qregistrysource.cpp b/src/remoteobjects/qregistrysource.cpp new file mode 100644 index 0000000..95fa12e --- /dev/null +++ b/src/remoteobjects/qregistrysource.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qregistrysource_p.h" + +QT_BEGIN_NAMESPACE + +QRegistrySource::QRegistrySource(QObject *parent) + : QRemoteObjectSource(parent) +{ + qRegisterMetaTypeStreamOperators<QRemoteObjectSourceLocation>(); + qRegisterMetaTypeStreamOperators<QRemoteObjectSourceLocations>(); +} + +QRegistrySource::~QRegistrySource() +{ +} + +QRemoteObjectSourceLocations QRegistrySource::sourceLocations() const +{ + qCDebug(QT_REMOTEOBJECT) << "sourceLocations property requested on RegistrySource" << m_sourceLocations; + return m_sourceLocations; +} + +void QRegistrySource::removeServer(const QUrl &url) +{ + QVector<QString> results; + typedef QRemoteObjectSourceLocations::const_iterator CustomIterator; + const CustomIterator end = m_sourceLocations.constEnd(); + for (CustomIterator it = m_sourceLocations.constBegin(); it != end; ++it) + if (it.value() == url) + results.push_back(it.key()); + Q_FOREACH (const QString &res, results) + m_sourceLocations.remove(res); +} + +void QRegistrySource::addSource(const QRemoteObjectSourceLocation &entry) +{ + qCDebug(QT_REMOTEOBJECT) << "An entry was added to the RegistrySource" << entry; + if (!m_sourceLocations.contains(entry.first)) { + m_sourceLocations[entry.first] = entry.second; + emit remoteObjectAdded(entry); + } +} + +void QRegistrySource::removeSource(const QRemoteObjectSourceLocation &entry) +{ + if (m_sourceLocations.contains(entry.first) && m_sourceLocations[entry.first] == entry.second) { + m_sourceLocations.remove(entry.first); + emit remoteObjectRemoved(entry); + } +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qregistrysource_p.h b/src/remoteobjects/qregistrysource_p.h new file mode 100644 index 0000000..f44b6d2 --- /dev/null +++ b/src/remoteobjects/qregistrysource_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREGISTRYSOURCE_P_H +#define QREGISTRYSOURCE_P_H + +#include "qremoteobjectsource.h" + +QT_BEGIN_NAMESPACE + +class QRegistrySource : public QRemoteObjectSource +{ + Q_OBJECT + Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_TYPE, "Registry") + + Q_PROPERTY(QRemoteObjectSourceLocations sourceLocations READ sourceLocations) + +public: + explicit QRegistrySource(QObject *parent = Q_NULLPTR); + ~QRegistrySource(); + + QRemoteObjectSourceLocations sourceLocations() const; + +Q_SIGNALS: + void remoteObjectAdded(const QRemoteObjectSourceLocation &entry); + void remoteObjectRemoved(const QRemoteObjectSourceLocation &entry); + +public Q_SLOTS: + void addSource(const QRemoteObjectSourceLocation &entry); + void removeSource(const QRemoteObjectSourceLocation &entry); + void removeServer(const QUrl &url); + +private: + QRemoteObjectSourceLocations m_sourceLocations; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qremoteobjectdynamicreplica.cpp b/src/remoteobjects/qremoteobjectdynamicreplica.cpp new file mode 100644 index 0000000..eb8aaac --- /dev/null +++ b/src/remoteobjects/qremoteobjectdynamicreplica.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qremoteobjectdynamicreplica.h" +#include "qremoteobjectreplica_p.h" + +#include <QDebug> +#include <QMetaProperty> + +QT_BEGIN_NAMESPACE + +QRemoteObjectDynamicReplica::QRemoteObjectDynamicReplica(QObject *parent) + : QRemoteObjectReplica(parent) +{ +} + +QRemoteObjectDynamicReplica::~QRemoteObjectDynamicReplica() +{ +} + +const QMetaObject* QRemoteObjectDynamicReplica::metaObject() const +{ + Q_D(const QRemoteObjectReplica); + + return d->m_metaObject ? d->m_metaObject : QRemoteObjectReplica::metaObject(); +} + +void *QRemoteObjectDynamicReplica::qt_metacast(const char *name) +{ + Q_D(QRemoteObjectReplica); + + if (!name) + return 0; + + if (!strcmp(name, "QRemoteObjectDynamicReplica")) + return static_cast<void*>(const_cast<QRemoteObjectDynamicReplica*>(this)); + + // not entirely sure that one is needed... TODO: check + if (QString::fromLatin1(name) == d->m_objectName) + return static_cast<void*>(const_cast<QRemoteObjectDynamicReplica*>(this)); + + return QObject::qt_metacast(name); +} + +int QRemoteObjectDynamicReplica::qt_metacall(QMetaObject::Call call, int id, void **argv) +{ + Q_D(QRemoteObjectReplica); + + int saved_id = id; + id = QRemoteObjectReplica::qt_metacall(call, id, argv); + if (id < 0 || d->m_metaObject == Q_NULLPTR) + return id; + + if (call == QMetaObject::ReadProperty || call == QMetaObject::WriteProperty) { + QMetaProperty mp = metaObject()->property(saved_id); + int &status = *reinterpret_cast<int *>(argv[2]); + + if (call == QMetaObject::WriteProperty) { + QVariantList args; + args << QVariant(mp.userType(), argv[0]); + QRemoteObjectReplica::send(QMetaObject::WriteProperty, saved_id, args); + } else { + const QVariant value = propAsVariant(id); + QMetaType::construct(mp.userType(), argv[0], value.data()); + const bool readStatus = true; + // Caller supports QVariant returns? Then we can also report errors + // by storing an invalid variant. + if (!readStatus && argv[1]) { + status = 0; + reinterpret_cast<QVariant*>(argv[1])->clear(); + } + } + + id = -1; + } else if (call == QMetaObject::InvokeMetaMethod) { + const int methodType = d->m_remoteObjectMethodTypes.at(id); + + if (methodType == QMetaMethod::Signal) { + // signal relay from Source world to Replica + QMetaObject::activate(this, d->m_metaObject, id, argv); + + } else if (methodType == QMetaMethod::Slot || methodType == QMetaMethod::Method) { + // method relay from Replica to Source + qCDebug(QT_REMOTEOBJECT) << "method" << d->m_metaObject->method(saved_id).name() << "invoked"; + + QVariantList args; + for (int i = 1; i <= d->m_methodArgumentTypes.at(id).size(); ++i) { + args << QVariant(d->m_methodArgumentTypes.at(id)[i-1], argv[i]); + } + + QRemoteObjectReplica::send(QMetaObject::InvokeMetaMethod, saved_id, args); + } + } + + return id; +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qremoteobjectdynamicreplica.h b/src/remoteobjects/qremoteobjectdynamicreplica.h new file mode 100644 index 0000000..e676468 --- /dev/null +++ b/src/remoteobjects/qremoteobjectdynamicreplica.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDYNAMICREMOTEOBJECT_H +#define QDYNAMICREMOTEOBJECT_H + +#include "qremoteobjectreplica.h" + +QT_BEGIN_NAMESPACE + +class Q_REMOTEOBJECTS_EXPORT QRemoteObjectDynamicReplica : public QRemoteObjectReplica +{ +public: + ~QRemoteObjectDynamicReplica(); + + const QMetaObject *metaObject() const Q_DECL_OVERRIDE; + void *qt_metacast(const char *name) Q_DECL_OVERRIDE; + int qt_metacall(QMetaObject::Call call, int id, void **argv) Q_DECL_OVERRIDE; + +private: + explicit QRemoteObjectDynamicReplica(QObject *parent = Q_NULLPTR); + friend class QRemoteObjectNodePrivate; + friend class QRemoteObjectNode; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qremoteobjectnode.cpp b/src/remoteobjects/qremoteobjectnode.cpp new file mode 100644 index 0000000..5e4924d --- /dev/null +++ b/src/remoteobjects/qremoteobjectnode.cpp @@ -0,0 +1,690 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qremoteobjectnode.h" +#include "qremoteobjectnode_p.h" + +#include "qremoteobjectregistry.h" +#include "qremoteobjectdynamicreplica.h" +#include "qregistrysource_p.h" +#include "qremoteobjectreplica_p.h" +#include "qremoteobjectsource_p.h" + +#include <QMetaProperty> + +QT_BEGIN_NAMESPACE + +static QString name(const QMetaObject * const mobj) +{ + const int ind = mobj->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE); + return ind >= 0 ? QString::fromLatin1(mobj->classInfo(ind).value()) : QString(); +} + +template <typename K, typename V, typename Query> +bool map_contains(const QMap<K,V> &map, const Query &key, typename QMap<K,V>::const_iterator &result) +{ + const typename QMap<K,V>::const_iterator it = map.find(key); + if (it == map.end()) + return false; + result = it; + return true; +} + +QRemoteObjectNodePrivate::QRemoteObjectNodePrivate() + : QObject(Q_NULLPTR) + , retryInterval(250) + , m_lastError(QRemoteObjectNode::NoError) +{ + connect(&clientRead, SIGNAL(mapped(QObject*)), this, SLOT(onClientRead(QObject*))); +} + +QRemoteObjectNodePrivate::~QRemoteObjectNodePrivate() +{ + Q_FOREACH (ClientIoDevice *conn, knownNodes) { + conn->close(); + conn->deleteLater(); + } +} + +QRemoteObjectSourceLocations QRemoteObjectNodePrivate::remoteObjectAddresses() const +{ + if (!registrySource.isNull()) + return registrySource->sourceLocations(); + else if (!registry.isNull()) + return registry->sourceLocations(); + return QRemoteObjectSourceLocations(); +} + +void QRemoteObjectNodePrivate::timerEvent(QTimerEvent*) +{ + Q_FOREACH (ClientIoDevice *conn, pendingReconnect) { + if (conn->isOpen()) + pendingReconnect.remove(conn); + else + conn->connectToServer(); + } + + if (pendingReconnect.isEmpty()) + reconnectTimer.stop(); + + qCDebug(QT_REMOTEOBJECT) << "timerEvent" << pendingReconnect.size(); +} + +QRemoteObjectReplica *QRemoteObjectNodePrivate::acquire(const QMetaObject *meta, QRemoteObjectReplica *instance, const QString &name) +{ + qCDebug(QT_REMOTEOBJECT) << "Starting acquire for" << name; + isInitialized.storeRelease(1); + openConnectionIfNeeded(name); + QMutexLocker locker(&mutex); + if (hasInstance(name)) { + qCDebug(QT_REMOTEOBJECT)<<"Acquire - using existing instance"; + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(name).toStrongRef(); + Q_ASSERT(rep); + instance->d_ptr = rep; + rep->configurePrivate(instance); + } else { + QMap<QString, QRemoteObjectSourcePrivate*>::const_iterator mapIt; + if (!remoteObjectIo.isNull() && map_contains(remoteObjectIo->m_remoteObjects, name, mapIt)) { + QInProcessReplicaPrivate *rp = new QInProcessReplicaPrivate(name, meta); + instance->d_ptr.reset(rp); + rp->configurePrivate(instance); + connectReplica(mapIt.value()->m_object, instance); + rp->connectionToSource = mapIt.value(); + } else { + QConnectedReplicaPrivate *rp = new QConnectedReplicaPrivate(name, meta); + instance->d_ptr.reset(rp); + rp->configurePrivate(instance); + if (connectedSources.contains(name)) { //Either we have a peer connections, or existing connection via registry + rp->setConnection(connectedSources[name]); + } else if (remoteObjectAddresses().contains(name)) { //No existing connection, but we know we can connect via registry + initConnection(remoteObjectAddresses()[name]); //This will try the connection, and if successful, the remoteObjects will be sent + //The link to the replica will be handled then + } + } + instance->initialize(); + replicas.insert(name, instance->d_ptr.toWeakRef()); + qCDebug(QT_REMOTEOBJECT) << "Acquire - Created new instance" << name<<remoteObjectAddresses(); + } + return instance; +} + +const QRemoteObjectRegistry *QRemoteObjectNode::registry() const +{ + return d_ptr->registry.data(); +} + +void QRemoteObjectNodePrivate::connectReplica(QObject *object, QRemoteObjectReplica *instance) +{ + int nConnections = 0; + const QMetaObject *us = instance->metaObject(); + const QMetaObject *them = object->metaObject(); + + static const int memberOffset = QRemoteObjectReplica::staticMetaObject.methodCount(); + for (int idx = memberOffset; idx < us->methodCount(); ++idx) { + const QMetaMethod mm = us->method(idx); + + qCDebug(QT_REMOTEOBJECT) << idx << mm.name(); + if (mm.methodType() != QMetaMethod::Signal) + continue; + + // try to connect to a signal on the parent that has the same method signature + QByteArray sig = QMetaObject::normalizedSignature(mm.methodSignature().constData()); + qCDebug(QT_REMOTEOBJECT) << sig; + if (them->indexOfSignal(sig.constData()) == -1) + continue; + + sig.prepend(QSIGNAL_CODE + '0'); + const char * const csig = sig.constData(); + const bool res = QObject::connect(object, csig, instance, csig); + Q_UNUSED(res); + ++nConnections; + + qCDebug(QT_REMOTEOBJECT) << sig << res; + } + + qCDebug(QT_REMOTEOBJECT) << "# connections =" << nConnections; +} + +void QRemoteObjectNodePrivate::openConnectionIfNeeded(const QString &name) +{ + qCDebug(QT_REMOTEOBJECT) << Q_FUNC_INFO << name << this; + if (remoteObjectAddresses().contains(name)) { + initConnection(remoteObjectAddresses()[name]); + qCDebug(QT_REMOTEOBJECT) << "openedConnection" << remoteObjectAddresses()[name]; + } +} + +void QRemoteObjectNodePrivate::initConnection(const QUrl &address) +{ + if (requestedUrls.contains(address)) { + qCWarning(QT_REMOTEOBJECT) << "Connection already initialized for " << address.toString(); + return; + } + + requestedUrls.insert(address); + + ClientIoDevice *connection = m_factory.createDevice(address, this); + Q_ASSERT_X(connection, Q_FUNC_INFO, "Could not create IODevice for client"); + + knownNodes.insert(connection); + qCDebug(QT_REMOTEOBJECT) << "Replica Connection isValid" << connection->isOpen(); + connect(connection, SIGNAL(shouldReconnect(ClientIoDevice*)), this, SLOT(onShouldReconnect(ClientIoDevice*))); + connection->connectToServer(); + connect(connection, SIGNAL(readyRead()), &clientRead, SLOT(map())); + clientRead.setMapping(connection, connection); +} + +bool QRemoteObjectNodePrivate::hasInstance(const QString &name) +{ + if (!replicas.contains(name)) + return false; + + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(name).toStrongRef(); + if (!rep) { //already deleted + replicas.remove(name); + return false; + } + + return true; +} + +void QRemoteObjectNodePrivate::onRemoteObjectSourceAdded(const QRemoteObjectSourceLocation &entry) +{ + qCDebug(QT_REMOTEOBJECT) <<"onRemoteObjectSourceAdded"<< entry << replicas<<replicas.contains(entry.first); + if (!entry.first.isEmpty()) { + QRemoteObjectSourceLocations locs = registry->propAsVariant(0).value<QRemoteObjectSourceLocations>(); + locs[entry.first] = entry.second; + //TODO Is there a way to extend QRemoteObjectSourceLocations in place? + registry->setProperty(0, QVariant::fromValue(locs)); + qCDebug(QT_REMOTEOBJECT) << "onRemoteObjectSourceAdded, now locations =" << registry->propAsVariant(0).value<QRemoteObjectSourceLocations>()<<locs; + } + if (replicas.contains(entry.first)) //We have a replica waiting on this remoteObject + { + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(entry.first).toStrongRef(); + if (!rep) { //replica has been deleted, remove from list + replicas.remove(entry.first); + return; + } + + initConnection(entry.second); + + qCDebug(QT_REMOTEOBJECT) << "Called initConnection due to new RemoteObjectSource added via registry" << entry.first; + } +} + +void QRemoteObjectNodePrivate::onRemoteObjectSourceRemoved(const QRemoteObjectSourceLocation &entry) +{ + if (!entry.first.isEmpty()) { + QRemoteObjectSourceLocations locs = registry->propAsVariant(0).value<QRemoteObjectSourceLocations>(); + locs.remove(entry.first); + registry->setProperty(0, QVariant::fromValue(locs)); + } +} + +void QRemoteObjectNodePrivate::onRegistryInitialized() +{ + qCDebug(QT_REMOTEOBJECT) << "Registry Initialized" << remoteObjectAddresses(); + + QHashIterator<QString, QUrl> i(remoteObjectAddresses()); + while (i.hasNext()) { + i.next(); + if (replicas.contains(i.key())) //We have a replica waiting on this remoteObject + { + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(i.key()).toStrongRef(); + if (rep && !requestedUrls.contains(i.value())) + initConnection(i.value()); + else if (!rep) //replica has been deleted, remove from list + replicas.remove(i.key()); + + continue; + } + } +} + +void QRemoteObjectNodePrivate::onShouldReconnect(ClientIoDevice *ioDevice) +{ + pendingReconnect.insert(ioDevice); + + Q_FOREACH (const QString &remoteObject, ioDevice->remoteObjects()) { + connectedSources.remove(remoteObject); + ioDevice->removeSource(remoteObject); + if (replicas.contains(remoteObject)) { //We have a replica waiting on this remoteObject + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(remoteObject).toStrongRef(); + QConnectedReplicaPrivate *cRep = static_cast<QConnectedReplicaPrivate*>(rep.data()); + if (rep && !cRep->connectionToSource.isNull()) { + cRep->setDisconnected(); + } else if (!rep) { + replicas.remove(remoteObject); + } + } + } + if (!reconnectTimer.isActive()) { + reconnectTimer.start(retryInterval, this); + qCDebug(QT_REMOTEOBJECT) << "Starting reconnect timer"; + } +} + +void QRemoteObjectNodePrivate::onClientRead(QObject *obj) +{ + ClientIoDevice *connection = qobject_cast<ClientIoDevice*>(obj); + Q_ASSERT(connection); + + if (!connection->read()) + return; + + using namespace QRemoteObjectPackets; + + const QRemoteObjectPacket* packet = connection->packet(); + switch (packet->id) { + case QRemoteObjectPacket::ObjectList: + { + const QObjectListPacket *p = static_cast<const QObjectListPacket *>(packet); + const QSet<QString> newObjects = p->objects.toSet(); + qCDebug(QT_REMOTEOBJECT) << "newObjects:" << newObjects; + Q_FOREACH (const QString &remoteObject, newObjects) { + qCDebug(QT_REMOTEOBJECT) << " connectedSources.contains("<<remoteObject<<")"<<connectedSources.contains(remoteObject)<<replicas.contains(remoteObject); + if (!connectedSources.contains(remoteObject)) { + connectedSources[remoteObject] = connection; + connection->addSource(remoteObject); + if (replicas.contains(remoteObject)) //We have a replica waiting on this remoteObject + { + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(remoteObject).toStrongRef(); + QConnectedReplicaPrivate *cRep = static_cast<QConnectedReplicaPrivate*>(rep.data()); + if (rep && cRep->connectionToSource.isNull()) + { + qCDebug(QT_REMOTEOBJECT) << "Test" << remoteObject<<replicas.keys(); + qCDebug(QT_REMOTEOBJECT) << cRep; + cRep->setConnection(connection); + } else if (!rep) { //replica has been deleted, remove from list + replicas.remove(remoteObject); + } + + continue; + } + } + } + break; + } + case QRemoteObjectPacket::InitPacket: + { + const QInitPacket *p = static_cast<const QInitPacket *>(packet); + const QString object = p->name; + qCDebug(QT_REMOTEOBJECT) << "InitObject-->" <<object << this; + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(object).toStrongRef(); + if (rep) + { + QConnectedReplicaPrivate *cRep = static_cast<QConnectedReplicaPrivate*>(rep.data()); + cRep->initialize(p->packetData); + } else { //replica has been deleted, remove from list + replicas.remove(object); + } + break; + } + case QRemoteObjectPacket::InitDynamicPacket: + { + const QInitDynamicPacket *p = static_cast<const QInitDynamicPacket *>(packet); + const QString object = p->name; + qCDebug(QT_REMOTEOBJECT) << "InitObject-->" <<object << this; + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(object).toStrongRef(); + if (rep) + { + QConnectedReplicaPrivate *cRep = static_cast<QConnectedReplicaPrivate*>(rep.data()); + cRep->initializeMetaObject(p); + + } else { //replica has been deleted, remove from list + replicas.remove(object); + } + break; + } + case QRemoteObjectPacket::RemoveObject: + { + const QRemoveObjectPacket *p = static_cast<const QRemoveObjectPacket *>(packet); + qCDebug(QT_REMOTEOBJECT) << "RemoveObject-->" << p->name << this; + connectedSources.remove(p->name); + connection->removeSource(p->name); + if (replicas.contains(p->name)) { //We have a replica waiting on this remoteObject + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(p->name).toStrongRef(); + QConnectedReplicaPrivate *cRep = static_cast<QConnectedReplicaPrivate*>(rep.data()); + if (rep && !cRep->connectionToSource.isNull()) { + cRep->connectionToSource.clear(); + if (cRep->isReplicaValid()) { + //Changed from receiving to not receiving + cRep->emitValidChanged(); + } + } else if (!rep) { + replicas.remove(p->name); + } + } + break; + } + case QRemoteObjectPacket::PropertyChangePacket: + { + const QPropertyChangePacket *p = static_cast<const QPropertyChangePacket *>(packet); + const QString object = p->name; + qCDebug(QT_REMOTEOBJECT) << "PropertyChange-->" << p->name << QString::fromLatin1(p->propertyName.constData()); + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(object).toStrongRef(); + if (rep) + { + const int offset = rep->m_metaObject->propertyOffset(); + const int index = rep->m_metaObject->indexOfProperty(p->propertyName.constData())-offset; + rep->setProperty(index, p->value); + } else { //replica has been deleted, remove from list + replicas.remove(p->name); + } + break; + } + case QRemoteObjectPacket::InvokePacket: + { + const QInvokePacket *p = static_cast<const QInvokePacket *>(packet); + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(p->name).toStrongRef(); + if (rep) + { + static QVariant null(QMetaType::QObjectStar, (void*)0); + + // Qt usually supports 9 arguments, so ten should be usually safe + QVarLengthArray<void*, 10> param(p->args.size() + 1); + param[0] = null.data(); //Never a return value + for (int i = 0; i < p->args.size(); i++) { + param[i + 1] = const_cast<void *>(p->args[i].data()); + } + qCDebug(QT_REMOTEOBJECT) << "Replica Invoke-->" << p->name << rep->m_metaObject->method(p->index+rep->m_methodOffset).name() << p->index << rep->m_methodOffset; + QMetaObject::activate(rep.data(), rep->metaObject(), p->index+rep->m_methodOffset, param.data()); + } else { //replica has been deleted, remove from list + replicas.remove(p->name); + } + break; + } + } + + if (connection->bytesAvailable()) //have bytes left over, so recurse + onClientRead(connection); +} + + +QRemoteObjectNode::QRemoteObjectNode() + : d_ptr(new QRemoteObjectNodePrivate) +{ + qRegisterMetaTypeStreamOperators<QVector<int> >(); +} + +QRemoteObjectNode::QRemoteObjectNode(const QUrl &hostAddress, const QUrl ®istryAddress) + : d_ptr(new QRemoteObjectNodePrivate) +{ + qRegisterMetaTypeStreamOperators<QVector<int> >(); + if (!hostAddress.isEmpty()) { + setHostUrl(hostAddress); + if (hostAddress == registryAddress) { + hostRegistry(); + return; + } + } + + if (!registryAddress.isEmpty()) + setRegistryUrl(registryAddress); +} + +QRemoteObjectNode::~QRemoteObjectNode() +{ +} + +QUrl QRemoteObjectNode::hostUrl() const +{ + if (d_ptr->remoteObjectIo.isNull()) + return QUrl(); + + return d_ptr->remoteObjectIo->serverAddress(); +} + +bool QRemoteObjectNode::setHostUrl(const QUrl &hostAddress) +{ + if (!d_ptr->remoteObjectIo.isNull()) { + d_ptr->m_lastError = ServerAlreadyCreated; + return false; + } + else if (d_ptr->isInitialized.loadAcquire()) { + d_ptr->m_lastError = RegistryAlreadyHosted; + return false; + } + + d_ptr->remoteObjectIo.reset(new QRemoteObjectSourceIo(hostAddress)); + //Since we don't know whether setHostUrl or setRegistryUrl/setRegistryHost will be called first, + //break it into two pieces. setHostUrl connects the RemoteObjectSourceIo->[add/remove]RemoteObjectSource to QRemoteObjectReplicaNode->[add/remove]RemoteObjectSource + //setRegistry* calls appropriately connect RemoteObjecSourcetIo->[add/remove]RemoteObjectSource to the registry when it is created + QObject::connect(d_ptr->remoteObjectIo.data(), SIGNAL(remoteObjectAdded(QRemoteObjectSourceLocation)), d_ptr.data(), SIGNAL(remoteObjectAdded(QRemoteObjectSourceLocation))); + QObject::connect(d_ptr->remoteObjectIo.data(), SIGNAL(remoteObjectRemoved(QRemoteObjectSourceLocation)), d_ptr.data(), SIGNAL(remoteObjectRemoved(QRemoteObjectSourceLocation))); + + return true; +} + +QRemoteObjectNode::ErrorCode QRemoteObjectNode::lastError() const +{ + return d_ptr->m_lastError; +} + +QUrl QRemoteObjectNode::registryUrl() const +{ + return d_ptr->registryAddress; +} + +bool QRemoteObjectNode::setRegistryUrl(const QUrl ®istryAddress) +{ + if (d_ptr->isInitialized.loadAcquire() || ! d_ptr->registry.isNull()) { + d_ptr->m_lastError = RegistryAlreadyHosted; + return false; + } + + connect(registryAddress); + d_ptr->registryAddress = registryAddress; + d_ptr->setRegistry(acquire<QRemoteObjectRegistry>()); + //Connect remoteObject[Added/Removed] to the registry Slot + QObject::connect(d_ptr.data(), SIGNAL(remoteObjectAdded(QRemoteObjectSourceLocation)), d_ptr->registry.data(), SLOT(addSource(QRemoteObjectSourceLocation))); + QObject::connect(d_ptr.data(), SIGNAL(remoteObjectRemoved(QRemoteObjectSourceLocation)), d_ptr->registry.data(), SLOT(removeSource(QRemoteObjectSourceLocation))); + //TODO - what should happen if you register a RemoteObjectSource on the Registry node, but the RegistrySource isn't connected? + //Possible to have a way to cache the values, so they can be sent when a connection is made + //Or, return false on enableRemoting, with an error about not having the registry? + //Or possible to get the list of RemoteObjectSources from remoteObjectIo, and send when the connection is made (use that as the cache) + return d_ptr->registry->waitForSource(); +} + +void QRemoteObjectNodePrivate::setRegistry(QRemoteObjectRegistry *reg) +{ + registry.reset(reg); + //Make sure when we get the registry initialized, we update our replicas + QObject::connect(reg, SIGNAL(initialized()), this, SLOT(onRegistryInitialized())); + //Make sure we handle new RemoteObjectSources on Registry... + QObject::connect(reg, SIGNAL(remoteObjectAdded(QRemoteObjectSourceLocation)), this, SLOT(onRemoteObjectSourceAdded(QRemoteObjectSourceLocation))); + QObject::connect(reg, SIGNAL(remoteObjectRemoved(QRemoteObjectSourceLocation)), this, SLOT(onRemoteObjectSourceRemoved(QRemoteObjectSourceLocation))); +} + +bool QRemoteObjectNode::hostRegistry() +{ + if (d_ptr->remoteObjectIo.isNull()) { + d_ptr->m_lastError = ServerAlreadyCreated; + return false; + } + else if (d_ptr->isInitialized.loadAcquire() || !d_ptr->registry.isNull()) { + d_ptr->m_lastError = RegistryAlreadyHosted; + return false; + } + + QRegistrySource *remoteObject = new QRegistrySource; + enableRemoting(remoteObject); + d_ptr->registryAddress = d_ptr->remoteObjectIo->serverAddress(); + d_ptr->registrySource.reset(remoteObject); + //Connect RemoteObjectSourceIo->remoteObject[Added/Removde] to the registry Slot + QObject::connect(d_ptr.data(), SIGNAL(remoteObjectAdded(QRemoteObjectSourceLocation)), d_ptr->registrySource.data(), SLOT(addSource(QRemoteObjectSourceLocation))); + QObject::connect(d_ptr.data(), SIGNAL(remoteObjectRemoved(QRemoteObjectSourceLocation)), d_ptr->registrySource.data(), SLOT(removeSource(QRemoteObjectSourceLocation))); + QObject::connect(d_ptr->remoteObjectIo.data(), SIGNAL(serverRemoved(QUrl)),d_ptr->registrySource.data(), SLOT(removeServer(QUrl))); + //onAdd/Remove update the known remoteObjects list in the RegistrySource, so no need to connect to the RegistrySource remoteObjectAdded/Removed signals + d_ptr->setRegistry(acquire<QRemoteObjectRegistry>()); + return true; +} + +QRemoteObjectNode QRemoteObjectNode::createHostNode(const QUrl &hostAddress) +{ + return QRemoteObjectNode(hostAddress, QUrl()); +} + +QRemoteObjectNode QRemoteObjectNode::createRegistryHostNode(const QUrl &hostAddress) +{ + return QRemoteObjectNode(hostAddress, hostAddress); +} + +QRemoteObjectNode QRemoteObjectNode::createNodeConnectedToRegistry(const QUrl ®istryAddress) +{ + return QRemoteObjectNode(QUrl(), registryAddress); +} + +QRemoteObjectNode QRemoteObjectNode::createHostNodeConnectedToRegistry(const QUrl &hostAddress, const QUrl ®istryAddress) +{ + if (hostAddress == registryAddress) { //Assume hosting registry is NOT intended + QRemoteObjectNode node(hostAddress, QUrl()); + node.d_ptr->m_lastError = UnintendedRegistryHosting; + return node; + } + + return QRemoteObjectNode(hostAddress, registryAddress); +} + +void QRemoteObjectNode::connect(const QUrl &address) +{ + d_ptr->initConnection(address); +} + +QRemoteObjectDynamicReplica *QRemoteObjectNode::acquire(const QString &name) +{ + QRemoteObjectDynamicReplica *instance = new QRemoteObjectDynamicReplica; + return static_cast<QRemoteObjectDynamicReplica*>(d_ptr->acquire(Q_NULLPTR, instance, name)); +} + +bool QRemoteObjectNode::enableRemoting(QRemoteObjectSource *remoteObject) +{ + if (d_ptr->remoteObjectIo.isNull()) { + d_ptr->m_lastError = OperationNotValidOnClientNode; + return false; + } + + const int ind = remoteObject->metaObject()->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE); + const QString name = QString::fromLatin1(remoteObject->metaObject()->classInfo(ind).value()); + + d_ptr->isInitialized.storeRelease(1); + + return d_ptr->remoteObjectIo->enableRemoting(remoteObject, &QRemoteObjectSource::staticMetaObject, name); +} + +bool QRemoteObjectNode::enableRemoting(QObject *object, const QMetaObject *_meta) +{ + if (d_ptr->remoteObjectIo.isNull()) { + d_ptr->m_lastError = OperationNotValidOnClientNode; + return false; + } + + const QMetaObject *meta = _meta; + QString name; + if (!meta) { //If meta isn't provided, we need to search for an object that has RemoteObject CLASSINFO + meta = object->metaObject(); + int ind = meta->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE); + while (meta && ind == -1) { + meta = meta->superClass(); + if (meta) + ind = meta->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE); + } + if (meta) { + name = QString::fromLatin1(meta->classInfo(ind).value()); + meta = meta->superClass(); //We want the parent of the class that has ClassInfo, since we want to forward + //the object_type API + } else { + name = object->objectName(); + if (name.isEmpty()) { + d_ptr->m_lastError = MissingObjectName; + qCWarning(QT_REMOTEOBJECT) << "enableRemoting() Error: Unable to Replicate an object that does not have objectName() set."; + return false; + } + meta = object->metaObject()->superClass(); //*Assume* we only want object's API forwarded + } + } else { + name = object->objectName(); + if (name.isEmpty()) { + d_ptr->m_lastError = MissingObjectName; + qCWarning(QT_REMOTEOBJECT) << "enableRemoting() Error: Unable to Replicate an object that does not have objectName() set."; + return false; + } + const QMetaObject *check = object->metaObject(); + if (check == meta) { + qCWarning(QT_REMOTEOBJECT) << "enableRemoting() Error: The QMetaObject pointer provided is for object. An ancestor of object should be used."; + return false; + } + while (check && check != meta) + check = check->superClass(); + if (!check) { //Oops, meta is not a superclass of object + qCWarning(QT_REMOTEOBJECT) << "enableRemoting() Error: The QMetaObject must be an ancestor of object."; + return false; + } + } + + return d_ptr->remoteObjectIo->enableRemoting(object, meta, name); +} + +bool QRemoteObjectNode::disableRemoting(QObject *remoteObject) +{ + if (d_ptr->remoteObjectIo.isNull()) { + d_ptr->m_lastError = OperationNotValidOnClientNode; + return false; + } + + QRemoteObjectSourcePrivate *pp = remoteObject->findChild<QRemoteObjectSourcePrivate *>(QString(), Qt::FindDirectChildrenOnly); + if (!pp) //We must be calling unregister before register was called. + return false; + + if (!d_ptr->remoteObjectIo->disableRemoting(pp)) { + d_ptr->m_lastError = SourceNotRegistered; + return false; + } + + return true; +} + +QRemoteObjectReplica *QRemoteObjectNode::acquire(const QMetaObject *replicaMeta, QRemoteObjectReplica *instance) +{ + return d_ptr->acquire(replicaMeta, instance, name(replicaMeta)); +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qremoteobjectnode.h b/src/remoteobjects/qremoteobjectnode.h new file mode 100644 index 0000000..2a5dced --- /dev/null +++ b/src/remoteobjects/qremoteobjectnode.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTEOBJECTNODE_H +#define QREMOTEOBJECTNODE_H + +#include "qtremoteobjectglobal.h" + +#include "qremoteobjectregistry.h" +#include "qremoteobjectdynamicreplica.h" + +#include <QSharedPointer> + +QT_BEGIN_NAMESPACE + +class QRemoteObjectSource; +class QRemoteObjectReplica; +class QRemoteObjectNodePrivate; + +class Q_REMOTEOBJECTS_EXPORT QRemoteObjectNode +{ +public: + + enum ErrorCode{ + NoError, + RegistryNotAcquired, + RegistryAlreadyHosted, + NodeIsNoServer, + ServerAlreadyCreated, + UnintendedRegistryHosting, + OperationNotValidOnClientNode, + SourceNotRegistered, + MissingObjectName + }; + + QRemoteObjectNode(); + ~QRemoteObjectNode(); + + //TODO maybe set defaults from a #define to allow override? + static QRemoteObjectNode createHostNode(const QUrl &hostAddress = QUrl(QString::fromLatin1("local:replica"))); + static QRemoteObjectNode createRegistryHostNode(const QUrl &hostAddress = QUrl(QString::fromLatin1("local:registry"))); + static QRemoteObjectNode createNodeConnectedToRegistry(const QUrl ®istryAddress = QUrl(QString::fromLatin1("local:registry"))); + static QRemoteObjectNode createHostNodeConnectedToRegistry(const QUrl &hostAddress = QUrl(QString::fromLatin1("local:replica")), + const QUrl ®istryAddress = QUrl(QString::fromLatin1("local:registry"))); + QUrl hostUrl() const; + bool setHostUrl(const QUrl &hostAddress); + QUrl registryUrl() const; + bool setRegistryUrl(const QUrl ®istryAddress); + bool hostRegistry(); + void connect(const QUrl &address=QUrl(QString::fromLatin1("local:replica"))); + const QRemoteObjectRegistry *registry() const; + template < class ObjectType > + ObjectType *acquire() + { + ObjectType* replica = new ObjectType; + return qobject_cast< ObjectType* >(acquire(&ObjectType::staticMetaObject, replica)); + } + QRemoteObjectDynamicReplica *acquire(const QString &name); + + bool enableRemoting(QRemoteObjectSource *remoteObject); + bool enableRemoting(QObject *object, const QMetaObject *meta = Q_NULLPTR); + bool disableRemoting(QObject *remoteObject); + + ErrorCode lastError() const; + +private: + QRemoteObjectNode(const QUrl &hostAddress, const QUrl ®istryAddress); + QRemoteObjectReplica *acquire(const QMetaObject *, QRemoteObjectReplica *); + + QSharedPointer<QRemoteObjectNodePrivate> d_ptr; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qremoteobjectnode_p.h b/src/remoteobjects/qremoteobjectnode_p.h new file mode 100644 index 0000000..aa28cef --- /dev/null +++ b/src/remoteobjects/qremoteobjectnode_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTEOBJECTNODE_P_H +#define QREMOTEOBJECTNODE_P_H + +#include "qconnectionclientfactory_p.h" +#include "qremoteobjectsourceio_p.h" +#include "qremoteobjectreplica.h" +#include "qremoteobjectnode.h" + +#include <QBasicTimer> +#include <QMutex> +#include <QSharedPointer> + +QT_BEGIN_NAMESPACE + +class QRemoteObjectRegistry; +class QRegistrySource; + +class QRemoteObjectNodePrivate : public QObject +{ + Q_OBJECT + +public: + QRemoteObjectNodePrivate(); + virtual ~QRemoteObjectNodePrivate(); + + QRemoteObjectSourceLocations remoteObjectAddresses() const; + + void timerEvent(QTimerEvent*); + + QRemoteObjectReplica *acquire(const QMetaObject *, QRemoteObjectReplica *, const QString &); + + void connectReplica(QObject *object, QRemoteObjectReplica *instance); + void openConnectionIfNeeded(const QString &name); + + void initConnection(const QUrl &address); + bool hasInstance(const QString &name); + void setRegistry(QRemoteObjectRegistry *); + +Q_SIGNALS: + void remoteObjectAdded(const QRemoteObjectSourceLocation &); + void remoteObjectRemoved(const QRemoteObjectSourceLocation &); + +public Q_SLOTS: + void onClientRead(QObject *obj); + void onRemoteObjectSourceAdded(const QRemoteObjectSourceLocation &entry); + void onRemoteObjectSourceRemoved(const QRemoteObjectSourceLocation &entry); + void onRegistryInitialized(); + void onShouldReconnect(ClientIoDevice *ioDevice); + +public: + QAtomicInt isInitialized; + QScopedPointer<QRemoteObjectSourceIo> remoteObjectIo; + QMutex mutex; + QUrl registryAddress; + QHash<QString, QWeakPointer<QRemoteObjectReplicaPrivate> > replicas; + QConnectionClientFactory m_factory; + QMap<QString, ClientIoDevice*> connectedSources; + QSet<ClientIoDevice*> knownNodes; + QSet<ClientIoDevice*> pendingReconnect; + QSet<QUrl> requestedUrls; + QSignalMapper clientRead; + QScopedPointer<QRemoteObjectRegistry> registry; + QScopedPointer<QRegistrySource> registrySource; + int retryInterval; + QBasicTimer reconnectTimer; + QRemoteObjectNode::ErrorCode m_lastError; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qremoteobjectregistry.cpp b/src/remoteobjects/qremoteobjectregistry.cpp new file mode 100644 index 0000000..f292070 --- /dev/null +++ b/src/remoteobjects/qremoteobjectregistry.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qremoteobjectregistry.h" +#include "qremoteobjectreplica_p.h" + +#include <QDataStream> + +QT_BEGIN_NAMESPACE + +QRemoteObjectRegistry::QRemoteObjectRegistry(QObject *parent) : QRemoteObjectReplica(parent) +{ +} + +QRemoteObjectRegistry::~QRemoteObjectRegistry() +{} + +void QRemoteObjectRegistry::initialize() +{ + qRegisterMetaType<QRemoteObjectSourceLocation>(); + qRegisterMetaTypeStreamOperators<QRemoteObjectSourceLocation>(); + qRegisterMetaType<QRemoteObjectSourceLocations>(); + qRegisterMetaTypeStreamOperators<QRemoteObjectSourceLocations>(); + QVariantList properties; + properties.reserve(3); + properties << QVariant::fromValue(QRemoteObjectSourceLocations()); + properties << QVariant::fromValue(QRemoteObjectSourceLocation()); + properties << QVariant::fromValue(QRemoteObjectSourceLocation()); + setProperties(properties); +} + +QRemoteObjectSourceLocations QRemoteObjectRegistry::sourceLocations() const +{ + return propAsVariant(0).value<QRemoteObjectSourceLocations>(); +} + +void QRemoteObjectRegistry::addSource(const QRemoteObjectSourceLocation &entry) +{ + if (!isInitialized()) { + bool res = waitForSource(); + if (!res) + return; //FIX What to do here? + } + qCDebug(QT_REMOTEOBJECT) << "An entry was added to the registry - Sending to Source" << entry.first << entry.second; + // This does not set any data to avoid a coherency problem between client and server + static int index = QRemoteObjectRegistry::staticMetaObject.indexOfMethod("addSource(QRemoteObjectSourceLocation)"); + QVariantList args; + args << QVariant::fromValue(entry); + send(QMetaObject::InvokeMetaMethod, index, args); +} + +void QRemoteObjectRegistry::removeSource(const QRemoteObjectSourceLocation &entry) +{ + qCDebug(QT_REMOTEOBJECT) << "An entry was removed from the registry - Sending to Source" << entry.first << entry.second; + // This does not set any data to avoid a coherency problem between client and server + static int index = QRemoteObjectRegistry::staticMetaObject.indexOfMethod("removeSource(QRemoteObjectSourceLocation)"); + QVariantList args; + args << QVariant::fromValue(entry); + send(QMetaObject::InvokeMetaMethod, index, args); +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qremoteobjectregistry.h b/src/remoteobjects/qremoteobjectregistry.h new file mode 100644 index 0000000..4e21cb8 --- /dev/null +++ b/src/remoteobjects/qremoteobjectregistry.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTEOBJECTREGISTRY_P_H +#define QREMOTEOBJECTREGISTRY_P_H + +#include "qremoteobjectreplica.h" + +QT_BEGIN_NAMESPACE + +class Q_REMOTEOBJECTS_EXPORT QRemoteObjectRegistry : public QRemoteObjectReplica +{ + Q_OBJECT + Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_TYPE, "Registry") + + Q_PROPERTY(QRemoteObjectSourceLocations sourceLocations READ sourceLocations) + + friend class QRemoteObjectNode; + +public: + ~QRemoteObjectRegistry(); + void initialize() Q_DECL_OVERRIDE; + + QRemoteObjectSourceLocations sourceLocations() const; + +Q_SIGNALS: + void remoteObjectAdded(const QRemoteObjectSourceLocation &entry); + void remoteObjectRemoved(const QRemoteObjectSourceLocation &entry); + +public Q_SLOTS: + void addSource(const QRemoteObjectSourceLocation &entry); + void removeSource(const QRemoteObjectSourceLocation &entry); + +private: + explicit QRemoteObjectRegistry(QObject *parent = Q_NULLPTR); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qremoteobjectreplica.cpp b/src/remoteobjects/qremoteobjectreplica.cpp new file mode 100644 index 0000000..1f34a16 --- /dev/null +++ b/src/remoteobjects/qremoteobjectreplica.cpp @@ -0,0 +1,404 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qremoteobjectreplica.h" +#include "qremoteobjectreplica_p.h" +#include "qremoteobjectdynamicreplica.h" +#include "qconnectionclientfactory_p.h" +#include "qremoteobjectsource_p.h" +#include "private/qmetaobjectbuilder_p.h" + +#include <QCoreApplication> +#include <QDataStream> +#include <QVariant> +#include <QMetaProperty> +#include <QThread> +#include <QTime> + +QT_BEGIN_NAMESPACE + +using namespace QRemoteObjectPackets; + +QRemoteObjectReplicaPrivate::QRemoteObjectReplicaPrivate(const QString &name, const QMetaObject *meta) + : QObject(Q_NULLPTR), m_objectName(name), m_metaObject(meta), + m_methodOffset(meta ? QRemoteObjectReplica::staticMetaObject.methodCount() : QRemoteObjectDynamicReplica::staticMetaObject.methodCount()), + m_propertyOffset(meta ? QRemoteObjectReplica::staticMetaObject.propertyCount() : QRemoteObjectDynamicReplica::staticMetaObject.propertyCount()) +{ +} + +QRemoteObjectReplicaPrivate::~QRemoteObjectReplicaPrivate() +{ + if (m_metaObject && qstrcmp(m_metaObject->className(), "QRemoteObjectDynamicReplica") == 0) + delete m_metaObject; +} + +QConnectedReplicaPrivate::QConnectedReplicaPrivate(const QString &name, const QMetaObject *meta) + : QRemoteObjectReplicaPrivate(name, meta), isSet(0), connectionToSource(Q_NULLPTR) +{ +} + +QConnectedReplicaPrivate::~QConnectedReplicaPrivate() +{ + if (!connectionToSource.isNull()) { + qCDebug(QT_REMOTEOBJECT) << "Replica deleted: sending RemoveObject to RemoteObjectSource" << m_objectName; + QRemoveObjectPacket packet(m_objectName); + sendCommand(&packet); + } +} + +bool QRemoteObjectReplicaPrivate::isDynamicReplica() const +{ + return m_metaObject == Q_NULLPTR; +} + +void QConnectedReplicaPrivate::sendCommand(const QRemoteObjectPacket *packet) +{ + Q_ASSERT(!connectionToSource.isNull()); + + if (!connectionToSource->isOpen()) + return; + + connectionToSource->write(packet->serialize()); +} + +void QConnectedReplicaPrivate::initialize(const QByteArray &packetData) +{ + qCDebug(QT_REMOTEOBJECT) << "initialize()" << m_propertyStorage.size(); + quint32 nParam, len; + QDataStream in(packetData); + in >> nParam; + QVector<int> signalList; + QVariant value; + const int offset = m_metaObject->propertyOffset(); + for (quint32 i = 0; i < nParam; ++i) { + in >> len; + qint64 pos = in.device()->pos(); + const int index = m_metaObject->indexOfProperty(packetData.constData()+pos) - offset; + in.skipRawData(len); //Skip property name char *, since we used it in-place ^^ + in >> value; + qCDebug(QT_REMOTEOBJECT) << " in loop" << index << m_propertyStorage.size(); + if (index >= 0 && m_propertyStorage[index] != value) { + m_propertyStorage[index] = value; + int notifyIndex = m_metaObject->property(index+offset).notifySignalIndex(); + if (notifyIndex >= 0) + signalList.append(notifyIndex); + } + qCDebug(QT_REMOTEOBJECT) << "SETPROPERTY" << index << m_metaObject->property(index+offset).name() << value.typeName() << value.toString(); + } + + //Note: Because we are generating the notify signals ourselves, we know there will be no parameters + //This allows us to pass noArgs to qt_metacall + void *noArgs[] = {0}; + Q_FOREACH (int index, signalList) { + qCDebug(QT_REMOTEOBJECT) << " Before activate" << index << m_metaObject->property(index).name(); + QMetaObject::activate(this, metaObject(), index, noArgs); + } + + //initialized and validChanged need to be sent manually, since they are not in the derived classes + if (isSet.fetchAndStoreRelease(2) > 0) { + //We are already initialized, now we are valid again + emitValidChanged(); + } else { + //We need to send the initialized signal, too + emitInitialized(); + emitValidChanged(); + } + qCDebug(QT_REMOTEOBJECT) << "isSet = true"; +} + +void QRemoteObjectReplicaPrivate::emitValidChanged() +{ + const static int validChangedIndex = QRemoteObjectReplica::staticMetaObject.indexOfMethod("onIsReplicaValidChanged()"); + Q_ASSERT(validChangedIndex != -1); + void *noArgs[] = {0}; + QMetaObject::activate(this, metaObject(), validChangedIndex, noArgs); +} + +void QRemoteObjectReplicaPrivate::emitInitialized() +{ + const static int initializedIndex = QRemoteObjectReplica::staticMetaObject.indexOfMethod("initialized()"); + Q_ASSERT(initializedIndex != -1); + void *noArgs[] = {0}; + QMetaObject::activate(this, metaObject(), initializedIndex, noArgs); +} + +void QRemoteObjectReplicaPrivate::initializeMetaObject(const QInitDynamicPacket *packet) +{ + Q_ASSERT(!m_metaObject); + + QMetaObjectBuilder builder; + builder.setClassName("QRemoteObjectDynamicReplica"); + builder.setSuperClass(&QRemoteObjectReplica::staticMetaObject); + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + + QVector<QPair<QByteArray, QVariant> > propertyValues; + m_metaObject = packet->createMetaObject(builder, m_remoteObjectMethodTypes, m_methodArgumentTypes, &propertyValues); + //rely on order of properties; + QVariantList list; + typedef QPair<QByteArray, QVariant> PropertyPair; + foreach (const PropertyPair &pair, propertyValues) + list << pair.second; + setProperties(list); +} + +void QConnectedReplicaPrivate::initializeMetaObject(const QInitDynamicPacket *packet) +{ + QRemoteObjectReplicaPrivate::initializeMetaObject(packet); + foreach (QRemoteObjectReplica *obj, m_parentsNeedingConnect) + configurePrivate(obj); + m_parentsNeedingConnect.clear(); + void *noArgs[] = {0}; + for (int index = 0; index < metaObject()->propertyCount(); ++index) { + qCDebug(QT_REMOTEOBJECT) << " Before activate" << index << m_metaObject->property(index).name(); + QMetaObject::activate(this, metaObject(), index, noArgs); + } + //initialized and validChanged need to be sent manually, since they are not in the derived classes + if (isSet.fetchAndStoreRelease(2) > 0) { + //We are already initialized, now we are valid again + emitValidChanged(); + } else { + //We need to send the initialized signal, too + emitInitialized(); + emitValidChanged(); + } + qCDebug(QT_REMOTEOBJECT) << "isSet = true"; +} + +bool QConnectedReplicaPrivate::isInitialized() const +{ + return isSet.load() > 0; +} + +bool QConnectedReplicaPrivate::isReplicaValid() const +{ + qCDebug(QT_REMOTEOBJECT) << "isReplicaValid()" << isSet.load(); + + return isSet.load() == 2; +} + +bool QConnectedReplicaPrivate::waitForSource(int timeout) +{ + if (isSet.load() != 2) { + QTime t; + t.start(); + + while (isSet.load() != 2) { + if (t.elapsed() > timeout) { + qCWarning(QT_REMOTEOBJECT) << "Timeout waiting for client to get set" << m_objectName; + return false; + } + + qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 10); + } + } + + return true; +} + +void QConnectedReplicaPrivate::_q_send(QMetaObject::Call call, int index, const QVariantList &args) +{ + Q_ASSERT(call == QMetaObject::InvokeMetaMethod || call == QMetaObject::WriteProperty); + + if (call == QMetaObject::InvokeMetaMethod) { + qCDebug(QT_REMOTEOBJECT) << "Send" << call << this->m_metaObject->method(index).name() << index << args << connectionToSource; + QInvokePacket package = QInvokePacket(m_objectName, call, index - m_methodOffset, args); + sendCommand(&package); + } else { + qCDebug(QT_REMOTEOBJECT) << "Send" << call << this->m_metaObject->property(index).name() << index << args << connectionToSource; + QInvokePacket package = QInvokePacket(m_objectName, call, index - m_propertyOffset, args); + sendCommand(&package); + } +} + +const QVariant QConnectedReplicaPrivate::getProperty(int i) const +{ + return m_propertyStorage[i]; +} + +void QConnectedReplicaPrivate::setProperties(const QVariantList &properties) +{ + Q_ASSERT(m_propertyStorage.isEmpty()); + m_propertyStorage.reserve(properties.length()); + m_propertyStorage = properties; +} + +void QConnectedReplicaPrivate::setProperty(int i, const QVariant &prop) +{ + m_propertyStorage[i] = prop; +} + +void QConnectedReplicaPrivate::setConnection(ClientIoDevice *conn) +{ + if (connectionToSource.isNull()) { + connectionToSource = conn; + qCDebug(QT_REMOTEOBJECT) << "setConnection started" << conn << m_objectName; + } + requestRemoteObjectSource(); +} + +void QConnectedReplicaPrivate::setDisconnected() +{ + connectionToSource.clear(); + if (isSet.fetchAndStoreRelease(1) == 2) + emitValidChanged(); +} + +void QConnectedReplicaPrivate::requestRemoteObjectSource() +{ + QAddObjectPacket packet(m_objectName, isDynamicReplica()); + sendCommand(&packet); +} + +void QRemoteObjectReplicaPrivate::configurePrivate(QRemoteObjectReplica *rep) +{ + for (int i = QRemoteObjectReplica::staticMetaObject.methodOffset(); i < m_metaObject->methodCount(); i++) { + QMetaMethod mm = m_metaObject->method(i); + if (mm.methodType() == QMetaMethod::Signal) { + const bool res = QMetaObject::connect(this, i, rep, i, Qt::DirectConnection, 0); + qCDebug(QT_REMOTEOBJECT) << " Connect"<<i<<res<<mm.name(); + Q_UNUSED(res); + } + } +} + +void QConnectedReplicaPrivate::configurePrivate(QRemoteObjectReplica *rep) +{ + if (m_metaObject) + QRemoteObjectReplicaPrivate::configurePrivate(rep); + else + m_parentsNeedingConnect.append(rep); +} + +QRemoteObjectReplica::QRemoteObjectReplica(QObject *parent) : QObject(parent) +{ +} + +QRemoteObjectReplica::~QRemoteObjectReplica() +{ +} + +void QRemoteObjectReplica::send(QMetaObject::Call call, int index, const QVariantList &args) +{ + Q_D(QRemoteObjectReplica); + + d->_q_send(call, index, args); +} + +const QVariant QRemoteObjectReplica::propAsVariant(int i) const +{ + Q_D(const QRemoteObjectReplica); + return d->getProperty(i); +} + +void QRemoteObjectReplica::setProperties(const QVariantList &properties) +{ + Q_D(QRemoteObjectReplica); + d->setProperties(properties); +} + +void QRemoteObjectReplica::setProperty(int i, const QVariant &prop) +{ + Q_D(QRemoteObjectReplica); + d->setProperty(i, prop); +} + +bool QRemoteObjectReplica::isInitialized() const +{ + Q_D(const QRemoteObjectReplica); + + return d->isInitialized(); +} + +void QRemoteObjectReplica::initialize() +{ +} + +bool QRemoteObjectReplica::isReplicaValid() const +{ + Q_D(const QRemoteObjectReplica); + + return d->isReplicaValid(); +} + +bool QRemoteObjectReplica::waitForSource(int timeout) +{ + Q_D(QRemoteObjectReplica); + + return d->waitForSource(timeout); +} + +QInProcessReplicaPrivate::QInProcessReplicaPrivate(const QString &name, const QMetaObject *meta) : QRemoteObjectReplicaPrivate(name, meta) +{ +} + +QInProcessReplicaPrivate::~QInProcessReplicaPrivate() +{ +} + +const QVariant QInProcessReplicaPrivate::getProperty(int i) const +{ + Q_ASSERT(connectionToSource); + Q_ASSERT(connectionToSource->m_object); + const int index = i + connectionToSource->m_propertyOffset; + Q_ASSERT(index >= 0 && index < connectionToSource->m_object->metaObject()->propertyCount()); + return connectionToSource->m_object->metaObject()->property(index).read(connectionToSource->m_object); +} + +void QInProcessReplicaPrivate::setProperties(const QVariantList &) +{ + //TODO some verification here maybe? +} + +void QInProcessReplicaPrivate::setProperty(int i, const QVariant &property) +{ + Q_ASSERT(connectionToSource); + Q_ASSERT(connectionToSource->m_object); + const int index = i+connectionToSource->m_propertyOffset; + Q_ASSERT(index >= 0 && index < connectionToSource->m_object->metaObject()->propertyCount()); + connectionToSource->m_object->metaObject()->property(index).write(connectionToSource->m_object, property); +} + +void QInProcessReplicaPrivate::_q_send(QMetaObject::Call call, int index, const QVariantList &args) +{ + connectionToSource->invoke(call, index, args); +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qremoteobjectreplica.h b/src/remoteobjects/qremoteobjectreplica.h new file mode 100644 index 0000000..0d0e92c --- /dev/null +++ b/src/remoteobjects/qremoteobjectreplica.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQREMOTEOBJECTREPLICA_H +#define QQREMOTEOBJECTREPLICA_H + +#include "qtremoteobjectglobal.h" + +#include <QSharedPointer> +#include <QtCore/qnamespace.h> + +QT_BEGIN_NAMESPACE + +class QRemoteObjectReplicaPrivate; + +class Q_REMOTEOBJECTS_EXPORT QRemoteObjectReplica : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool isReplicaValid READ isReplicaValid NOTIFY onIsReplicaValidChanged) + +public: + virtual ~QRemoteObjectReplica(); + + bool isReplicaValid() const; + bool waitForSource(int timeout = 100); + bool isInitialized() const; + virtual void initialize(); + +Q_SIGNALS: + void onIsReplicaValidChanged(); + void initialized(); + +protected: + explicit QRemoteObjectReplica(QObject *parent = Q_NULLPTR); + + void send(QMetaObject::Call call, int index, const QVariantList &args); +protected: + void setProperty(int i, const QVariant &); + void setProperties(const QVariantList &); + const QVariant propAsVariant(int i) const; + Q_DECLARE_PRIVATE(QRemoteObjectReplica) + QSharedPointer<QRemoteObjectReplicaPrivate> d_ptr; +private: + friend class QRemoteObjectNodePrivate; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qremoteobjectreplica_p.h b/src/remoteobjects/qremoteobjectreplica_p.h new file mode 100644 index 0000000..f2451e3 --- /dev/null +++ b/src/remoteobjects/qremoteobjectreplica_p.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTEOBJECTREPLICA_P_H +#define QREMOTEOBJECTREPLICA_P_H + +#include "qremoteobjectreplica.h" +#include <QPointer> +#include <QVector> +#include <qcompilerdetection.h> +#include "qtremoteobjectglobal.h" + +QT_BEGIN_NAMESPACE + +class QRemoteObjectReplica; +class QRemoteObjectSourcePrivate; +class ClientIoDevice; + +using namespace QRemoteObjectPackets; + +class QRemoteObjectReplicaPrivate : public QObject +{ +public: + explicit QRemoteObjectReplicaPrivate(const QString &name, const QMetaObject *); + virtual ~QRemoteObjectReplicaPrivate(); + + bool isDynamicReplica() const; + + virtual const QVariant getProperty(int i) const = 0; + virtual void setProperties(const QVariantList &) = 0; + virtual void setProperty(int i, const QVariant &) = 0; + virtual bool isShortCircuit() const = 0; + virtual bool isInitialized() const { return true; } + virtual bool isReplicaValid() const { return true; } + virtual bool waitForSource(int) { return true; } + virtual void configurePrivate(QRemoteObjectReplica *); + void emitValidChanged(); + void emitInitialized(); + + virtual void _q_send(QMetaObject::Call call, int index, const QVariantList &args) = 0; + + //Dynamic replica functions + virtual void initializeMetaObject(const QInitDynamicPacket *packet); + + QString m_objectName; + const QMetaObject *m_metaObject; + + //Dynamic Replica data + QVector<QVector<int> > m_methodArgumentTypes; + QVector<int> m_remoteObjectMethodTypes; + int m_methodOffset, m_propertyOffset; +}; + +class QConnectedReplicaPrivate : public QRemoteObjectReplicaPrivate +{ +public: + explicit QConnectedReplicaPrivate(const QString &name, const QMetaObject *); + virtual ~QConnectedReplicaPrivate(); + const QVariant getProperty(int i) const Q_DECL_OVERRIDE; + void setProperties(const QVariantList &) Q_DECL_OVERRIDE; + void setProperty(int i, const QVariant &) Q_DECL_OVERRIDE; + bool isShortCircuit() const Q_DECL_OVERRIDE { return false; } + bool isInitialized() const Q_DECL_OVERRIDE; + bool isReplicaValid() const Q_DECL_OVERRIDE; + bool waitForSource(int timeout) Q_DECL_OVERRIDE; + void initialize(const QByteArray &); + void configurePrivate(QRemoteObjectReplica *) Q_DECL_OVERRIDE; + void requestRemoteObjectSource(); + void sendCommand(const QRemoteObjectPackets::QRemoteObjectPacket *packet); + void setConnection(ClientIoDevice *conn); + void setDisconnected(); + void _q_send(QMetaObject::Call call, int index, const QVariantList &args) Q_DECL_OVERRIDE; + void initializeMetaObject(const QInitDynamicPacket *packet) Q_DECL_OVERRIDE; + QAtomicInt isSet; + QVector<QRemoteObjectReplica *> m_parentsNeedingConnect; + QVariantList m_propertyStorage; + QPointer<ClientIoDevice> connectionToSource; +}; + +class QInProcessReplicaPrivate : public QRemoteObjectReplicaPrivate +{ +public: + explicit QInProcessReplicaPrivate(const QString &name, const QMetaObject *); + virtual ~QInProcessReplicaPrivate(); + + const QVariant getProperty(int i) const Q_DECL_OVERRIDE; + void setProperties(const QVariantList &) Q_DECL_OVERRIDE; + void setProperty(int i, const QVariant &) Q_DECL_OVERRIDE; + bool isShortCircuit() const Q_DECL_OVERRIDE { return true; } + + virtual void _q_send(QMetaObject::Call call, int index, const QVariantList &args); + QPointer<QRemoteObjectSourcePrivate> connectionToSource; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qremoteobjectsource.cpp b/src/remoteobjects/qremoteobjectsource.cpp new file mode 100644 index 0000000..ca03e18 --- /dev/null +++ b/src/remoteobjects/qremoteobjectsource.cpp @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qremoteobjectsource.h" +#include "qremoteobjectsource_p.h" + +#include "qconnectionabstractserver_p.h" +#include "qtremoteobjectglobal.h" + +#include <QMetaProperty> +#include <QVarLengthArray> + +#include <algorithm> +#include <iterator> + +QT_BEGIN_NAMESPACE + +using namespace QRemoteObjectPackets; + +static QVector<int> parameter_types(const QMetaMethod &member) +{ + QVector<int> types; + types.reserve(member.parameterCount()); + for (int i = 0; i < member.parameterCount(); ++i) { + const int tp = member.parameterType(i); + if (tp == QMetaType::UnknownType) { + Q_ASSERT(tp != QMetaType::Void); // void parameter => metaobject is corrupt + qCWarning(QT_REMOTEOBJECT) <<"Don't know how to handle " + << member.parameterTypes().at(i).constData() + << ", use qRegisterMetaType to register it."; + + } + types << tp; + } + return types; +} + +QRemoteObjectSourcePrivate::QRemoteObjectSourcePrivate(QObject *obj, QMetaObject const *meta, const QString &name) + : QObject(obj), + args(meta->methodCount()), + m_name(name), + m_object(obj), + m_meta(meta), + m_methodOffset(meta == obj->metaObject() ? meta->methodOffset() : meta->methodCount()), + m_propertyOffset(meta == obj->metaObject() ? meta->propertyOffset() : meta->propertyCount()) +{ + if (!obj) { + qCWarning(QT_REMOTEOBJECT) << "QRemoteObjectSourcePrivate: Cannot replicate a NULL object" << name; + return; + } + + const QMetaObject *them = obj->metaObject(); + for (int idx = m_propertyOffset; idx < them->propertyCount(); ++idx) { + const QMetaProperty mp = them->property(idx); + qCDebug(QT_REMOTEOBJECT) << "Property option" << idx << mp.name() << mp.isWritable() << mp.hasNotifySignal() << mp.notifySignalIndex(); + if (mp.hasNotifySignal()) + propertyFromNotifyIndex.insert(mp.notifySignalIndex(), mp); + } + + args.reserve(them->methodCount() - m_methodOffset); + + for (int idx = m_methodOffset; idx < them->methodCount(); ++idx) { + const QMetaMethod mm = them->method(idx); + qCDebug(QT_REMOTEOBJECT) << "Connection option" << idx << mm.name(); + + if (mm.methodType() != QMetaMethod::Signal) + continue; + + // This basically connects the parent Signals (note, all dynamic properties have onChange + //notifications, thus signals) to us. Normally each Signal is mapped to a unique index, + //but since we are forwarding them all, we keep the offset constant. + // + //We know no one will inherit from this class, so no need to worry about indices from + //derived classes. + if (!QMetaObject::connect(obj, idx, this, m_methodOffset, Qt::DirectConnection, 0)) { + qCWarning(QT_REMOTEOBJECT) << "QRemoteObjectSourcePrivate: QMetaObject::connect returned false. Unable to connect."; + return; + } + + args.push_back(parameter_types(mm)); + + qCDebug(QT_REMOTEOBJECT) << "Connection made" << idx << mm.name(); + } +} + +QRemoteObjectSourcePrivate::~QRemoteObjectSourcePrivate() +{ + Q_FOREACH (ServerIoDevice *io, listeners) { + removeListener(io, true); + } +} + +QVariantList QRemoteObjectSourcePrivate::marshalArgs(int index, void **a) +{ + QVariantList list; + const QVector<int> &argsForIndex = args[index]; + const int N = argsForIndex.size(); + list.reserve(N); + for (int i = 0; i < N; ++i) { + const int type = argsForIndex[i]; + if (type == QMetaType::QVariant) + list << *reinterpret_cast<QVariant *>(a[i + 1]); + else + list << QVariant(type, a[i + 1]); + } + return list; +} + +void QRemoteObjectSourcePrivate::invoke(QMetaObject::Call c, int index, const QVariantList &args) +{ + static QVariant null(QMetaType::QObjectStar, Q_NULLPTR); + QVarLengthArray<void*, 10> param(args.size() + 1); + param[0] = null.data(); //Never a return value + for (int i = 0; i < args.size(); ++i) { + param[i + 1] = const_cast<void*>(args.at(i).data()); + } + if (c == QMetaObject::InvokeMetaMethod) { + parent()->qt_metacall(c, index + m_methodOffset, param.data()); + } else { + parent()->qt_metacall(c, index + m_propertyOffset, param.data()); + } +} + +#ifdef Q_COMPILER_UNIFORM_INIT +// QPair (like any class) can be initialized with { } +typedef QPair<QString,QVariant> Pair; +inline Pair make_pair(QString first, QVariant second) +{ return { qMove(first), qMove(second) }; } +#else +// QPair can't be initialized with { }, need to use a POD +struct Pair { + QString first; + QVariant second; +}; +inline QDataStream &operator<<(QDataStream &s, const Pair &p) +{ return s << p.first << p.second; } +inline Pair make_pair(QString first, QVariant second) +{ Pair p = { qMove(first), qMove(second) }; return p; } +#endif + +void QRemoteObjectSourcePrivate::handleMetaCall(int index, QMetaObject::Call call, void **a) +{ + if (listeners.empty()) + return; + + QByteArray ba; + + if (propertyFromNotifyIndex.contains(index)) { + const QMetaProperty mp = propertyFromNotifyIndex[index]; + qCDebug(QT_REMOTEOBJECT) << "Invoke Property" << mp.name() << mp.read(m_object); + QPropertyChangePacket p(m_name, mp.name(), mp.read(m_object)); + ba = p.serialize(); + } + + qCDebug(QT_REMOTEOBJECT) << "# Listeners" << listeners.length(); + qCDebug(QT_REMOTEOBJECT) << "Invoke args:" << m_object << call << index << marshalArgs(index, a); + QInvokePacket p(m_name, call, index - m_methodOffset, marshalArgs(index, a)); + + ba += p.serialize(); + + Q_FOREACH (ServerIoDevice *io, listeners) + io->write(ba); +} + +void QRemoteObjectSourcePrivate::addListener(ServerIoDevice *io, bool dynamic) +{ + listeners.append(io); + + if (dynamic) { + QRemoteObjectPackets::QInitDynamicPacketEncoder p(m_name, m_object, m_meta); + io->write(p.serialize()); + } else { + QRemoteObjectPackets::QInitPacketEncoder p(m_name, m_object, m_meta); + io->write(p.serialize()); + } +} + +int QRemoteObjectSourcePrivate::removeListener(ServerIoDevice *io, bool shouldSendRemove) +{ + listeners.removeAll(io); + if (shouldSendRemove) + { + QRemoveObjectPacket p(m_name); + io->write(p.serialize()); + } + return listeners.length(); +} + +int QRemoteObjectSourcePrivate::qt_metacall(QMetaObject::Call call, int methodId, void **a) +{ + //We get called from the stored metaobject metacall. Thus our index won't just be the index within our type, it will include + //an offset for any signals/slots in the meta base class. The delta offset accounts for this. + const int delta = m_meta->methodCount() - m_methodOffset; + + methodId = QObject::qt_metacall(call, methodId, a); + if (methodId < 0) + return methodId; + + if (call == QMetaObject::InvokeMetaMethod) { + if (methodId >= delta) { + handleMetaCall(senderSignalIndex(), call, a); + } + --methodId; + } + + return methodId; +} + + +QRemoteObjectSource::QRemoteObjectSource(QObject *parent) + : QObject(parent) + , d_ptr(Q_NULLPTR) +{ + emit initialized(); +} + +QRemoteObjectSource::~QRemoteObjectSource() +{ +} + +bool QRemoteObjectSource::isReplicaValid() const +{ + return true; +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qremoteobjectsource.h b/src/remoteobjects/qremoteobjectsource.h new file mode 100644 index 0000000..adf4a15 --- /dev/null +++ b/src/remoteobjects/qremoteobjectsource.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTEOBJECTSOURCE_H +#define QREMOTEOBJECTSOURCE_H + +#include "qtremoteobjectglobal.h" + +#include <QScopedPointer> + +QT_BEGIN_NAMESPACE + +class QRemoteObjectSourcePrivate; + +class Q_REMOTEOBJECTS_EXPORT QRemoteObjectSource : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool isReplicaValid READ isReplicaValid NOTIFY onIsReplicaValidChanged) + +public: + explicit QRemoteObjectSource(QObject *parent = Q_NULLPTR); + virtual ~QRemoteObjectSource(); + + bool isReplicaValid() const; + +Q_SIGNALS: + void onIsReplicaValidChanged(); + void initialized(); + +private: + friend class QRemoteObjectSourceIo; + Q_DECLARE_PRIVATE(QRemoteObjectSource) + + QScopedPointer<QRemoteObjectSourcePrivate> d_ptr; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qremoteobjectsource_p.h b/src/remoteobjects/qremoteobjectsource_p.h new file mode 100644 index 0000000..3592034 --- /dev/null +++ b/src/remoteobjects/qremoteobjectsource_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTEOBJECTSOURCE_P_H +#define QREMOTEOBJECTSOURCE_P_H + +#include <QObject> + +#include <QVector> + +QT_BEGIN_NAMESPACE + +class ServerIoDevice; +class QRemoteObjectSource; + +template <typename Container> +class ContainerWithOffset { +public: + typedef typename Container::value_type value_type; + typedef typename Container::reference reference; + typedef typename Container::const_reference const_reference; + typedef typename Container::difference_type difference_type; + typedef typename Container::pointer pointer; + typedef typename Container::const_pointer const_pointer; + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + + ContainerWithOffset() :c(), offset(0) {} + ContainerWithOffset(off_t offset) + : c(), offset(offset) {} + ContainerWithOffset(const Container &c, off_t offset) + : c(c), offset(offset) {} +#ifdef Q_COMPILER_RVALUE_REFS + ContainerWithOffset(Container &&c, off_t offset) + : c(std::move(c)), offset(offset) {} +#endif + + reference operator[](int i) { return c[i-offset]; }; + const_reference operator[](int i) const { return c[i-offset]; } + + void reserve(int size) { c.reserve(size); } + void push_back(const value_type &v) { c.push_back(v); } +#ifdef Q_COMPILER_RVALUE_REFS + void push_back(value_type &&v) { c.push_back(std::move(v)); } +#endif + +private: + Container c; + off_t offset; +}; + +class QRemoteObjectSourcePrivate : public QObject +{ +public: + explicit QRemoteObjectSourcePrivate(QObject *object, QMetaObject const *meta, const QString &name); + + ~QRemoteObjectSourcePrivate(); + + int qt_metacall(QMetaObject::Call call, int methodId, void **a); + ContainerWithOffset<QVector<QVector<int> > > args; + QHash<int, QMetaProperty> propertyFromNotifyIndex; + QList<ServerIoDevice*> listeners; + QString m_name; + QObject *m_object; + const QMetaObject * const m_meta; + const int m_methodOffset; + const int m_propertyOffset; + + QVariantList marshalArgs(int index, void **a); + void handleMetaCall(int index, QMetaObject::Call call, void **a); + void addListener(ServerIoDevice *io, bool dynamic = false); + int removeListener(ServerIoDevice *io, bool shouldSendRemove = false); + void invoke(QMetaObject::Call, int index, const QVariantList &args); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qremoteobjectsourceio.cpp b/src/remoteobjects/qremoteobjectsourceio.cpp new file mode 100644 index 0000000..51bd205 --- /dev/null +++ b/src/remoteobjects/qremoteobjectsourceio.cpp @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qremoteobjectsourceio_p.h" + +#include "qremoteobjectsource.h" +#include "qremoteobjectsource_p.h" +#include "qtremoteobjectglobal.h" + +#include <QMetaClassInfo> +#include <QStringList> + +QT_BEGIN_NAMESPACE + +QRemoteObjectSourceIo::QRemoteObjectSourceIo(const QUrl &address) + : m_server(m_factory.createServer(address, this)) +{ + if (m_server->listen(address)) { + qCDebug(QT_REMOTEOBJECT) << "QRemoteObjectSourceIo is Listening" << address; + } else { + qCDebug(QT_REMOTEOBJECT) << "Listen failed"; + qCDebug(QT_REMOTEOBJECT) << address; + qCDebug(QT_REMOTEOBJECT) << m_server->serverError(); + } + + connect(m_server.data(), SIGNAL(newConnection()), this, SLOT(handleConnection())); + connect(&m_serverDelete, SIGNAL(mapped(QObject*)), this, SLOT(onServerDisconnect(QObject*))); + connect(&m_serverRead, SIGNAL(mapped(QObject*)), this, SLOT(onServerRead(QObject*))); + connect(&m_remoteObjectDestroyed, SIGNAL(mapped(QString)), this, SLOT(clearRemoteObjectSource(QString))); +} + +QRemoteObjectSourceIo::~QRemoteObjectSourceIo() +{ + Q_FOREACH (QRemoteObjectSourcePrivate *pp, m_remoteObjects) { + disableRemoting(pp); + } +} + +bool QRemoteObjectSourceIo::enableRemoting(QObject *object, const QMetaObject *meta, const QString &name) +{ + if (m_remoteObjects.contains(name)) { + qCWarning(QT_REMOTEOBJECT) << "Tried to register QRemoteObjectSource twice" << name; + return false; + } + + QRemoteObjectSourcePrivate *pp = new QRemoteObjectSourcePrivate(object, meta, name); + + qCDebug(QT_REMOTEOBJECT) << "Registering" << name; + + m_remoteObjects[name] = pp; + connect(pp, SIGNAL(destroyed()), &m_remoteObjectDestroyed, SLOT(map())); + m_remoteObjectDestroyed.setMapping(pp, name); + emit remoteObjectAdded(qMakePair(name, m_server->address())); + + return true; +} + +bool QRemoteObjectSourceIo::disableRemoting(QRemoteObjectSourcePrivate *pp) +{ + const QString name = pp->m_name; + clearRemoteObjectSource(name); + pp->setParent(Q_NULLPTR); + delete pp; + + return true; +} + +void QRemoteObjectSourceIo::onServerDisconnect(QObject *conn) +{ + ServerIoDevice *connection = qobject_cast<ServerIoDevice*>(conn); + m_connections.remove(connection); + + qCDebug(QT_REMOTEOBJECT) << "OnServerDisconnect"; + + Q_FOREACH (QRemoteObjectSourcePrivate *pp, m_remoteObjects) + pp->removeListener(connection); + + const QUrl location = m_registryMapping.value(connection); + emit serverRemoved(location); + m_registryMapping.remove(connection); + connection->close(); + connection->deleteLater(); +} + +void QRemoteObjectSourceIo::onServerRead(QObject *conn) +{ + // Assert the invariant here conn is of type QIODevice + ServerIoDevice *connection = qobject_cast<ServerIoDevice*>(conn); + + do { + + if (!connection->read()) + return; + + using namespace QRemoteObjectPackets; + + const QRemoteObjectPacket* packet = connection->packet(); + switch (packet->id) { + case QRemoteObjectPacket::AddObject: + { + const QAddObjectPacket *p = static_cast<const QAddObjectPacket *>(packet); + const QString name = p->name; + qCDebug(QT_REMOTEOBJECT) << "AddObject" << name << p->isDynamic; + if (m_remoteObjects.contains(name)) { + QRemoteObjectSourcePrivate *pp = m_remoteObjects[name]; + pp->addListener(connection, p->isDynamic); + } else { + qCWarning(QT_REMOTEOBJECT) << "Request to attach to non-existent RemoteObjectSource:" << name; + } + break; + } + case QRemoteObjectPacket::RemoveObject: + { + const QRemoveObjectPacket *p = static_cast<const QRemoveObjectPacket *>(packet); + const QString name = p->name; + qCDebug(QT_REMOTEOBJECT) << "RemoveObject" << name; + if (m_remoteObjects.contains(name)) { + QRemoteObjectSourcePrivate *pp = m_remoteObjects[name]; + const int count = pp->removeListener(connection); + Q_UNUSED(count); + //TODO - possible to have a timer that closes connections if not reopened within a timeout? + } else { + qCWarning(QT_REMOTEOBJECT) << "Request to detach from non-existent RemoteObjectSource:" << name; + } + qCDebug(QT_REMOTEOBJECT) << "RemoveObject finished" << name; + break; + } + case QRemoteObjectPacket::InvokePacket: + { + const QInvokePacket *p = static_cast<const QInvokePacket *>(packet); + const QString name = p->name; + if (name == QStringLiteral("Registry") && !m_registryMapping.contains(connection)) { + const QRemoteObjectSourceLocation loc = p->args.first().value<QRemoteObjectSourceLocation>(); + m_registryMapping[connection] = loc.second; + } + if (m_remoteObjects.contains(name)) { + QRemoteObjectSourcePrivate *pp = m_remoteObjects[name]; + if (p->call == QMetaObject::InvokeMetaMethod) { + qCDebug(QT_REMOTEOBJECT) << "Source (method) Invoke-->" << name << pp->m_meta->method(p->index+pp->m_methodOffset).name(); + pp->invoke(QMetaObject::InvokeMetaMethod, p->index, p->args); + } else { + qCDebug(QT_REMOTEOBJECT) << "Source (write property) Invoke-->" << name << pp->m_meta->property(p->index+pp->m_propertyOffset).name(); + pp->invoke(QMetaObject::WriteProperty, p->index, p->args); + } + } + break; + } + default: + qCDebug(QT_REMOTEOBJECT) << "OnReadReady invalid type" << packet->id; + } + } while (connection->bytesAvailable()); // have bytes left over, so do another iteration +} + +void QRemoteObjectSourceIo::handleConnection() +{ + qCDebug(QT_REMOTEOBJECT) << "handleConnection" << m_connections; + + ServerIoDevice *conn = m_server->nextPendingConnection(); + m_connections.insert(conn); + connect(conn, SIGNAL(disconnected()), &m_serverDelete, SLOT(map())); + m_serverDelete.setMapping(conn, conn); + connect(conn, SIGNAL(readyRead()), &m_serverRead, SLOT(map())); + m_serverRead.setMapping(conn, conn); + + QRemoteObjectPackets::QObjectListPacket p(QStringList(m_remoteObjects.keys())); + conn->write(p.serialize()); + qCDebug(QT_REMOTEOBJECT) << "Wrote ObjectList packet from Server"; +} + +void QRemoteObjectSourceIo::clearRemoteObjectSource(const QString &name) +{ + m_remoteObjects.remove(name); + emit remoteObjectRemoved(qMakePair(name, serverAddress())); +} + +QUrl QRemoteObjectSourceIo::serverAddress() const +{ + return m_server->address(); +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qremoteobjectsourceio_p.h b/src/remoteobjects/qremoteobjectsourceio_p.h new file mode 100644 index 0000000..88ac68d --- /dev/null +++ b/src/remoteobjects/qremoteobjectsourceio_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTEOBJECTSOURCEIO_P_H +#define QREMOTEOBJECTSOURCEIO_P_H + +#include "qconnectionserverfactory_p.h" +#include "qtremoteobjectglobal.h" + +#include <QIODevice> +#include <QScopedPointer> +#include <QSignalMapper> + +QT_BEGIN_NAMESPACE + +class QRemoteObjectSource; +class QRemoteObjectSourcePrivate; + +class QRemoteObjectSourceIo : public QObject +{ + Q_OBJECT +public: + explicit QRemoteObjectSourceIo(const QUrl &address); + ~QRemoteObjectSourceIo(); + + bool enableRemoting(QObject *object, const QMetaObject *meta, const QString &name); + bool disableRemoting(QRemoteObjectSourcePrivate *pp); + + QUrl serverAddress() const; + +public Q_SLOTS: + void handleConnection(); + void onServerDisconnect(QObject *obj = Q_NULLPTR); + void onServerRead(QObject *obj); + void clearRemoteObjectSource(const QString &name); + +Q_SIGNALS: + void remoteObjectAdded(const QRemoteObjectSourceLocation &); + void remoteObjectRemoved(const QRemoteObjectSourceLocation &); + void serverRemoved(const QUrl& url); + +private: + friend class QRemoteObjectNodePrivate; + QHash<QIODevice*, quint32> m_readSize; + QConnectionServerFactory m_factory; + QSet<ServerIoDevice*> m_connections; + QMap<QString, QRemoteObjectSourcePrivate*> m_remoteObjects; + QSignalMapper m_serverDelete; + QSignalMapper m_serverRead; + QSignalMapper m_remoteObjectDestroyed; + QHash<ServerIoDevice*, QUrl> m_registryMapping; + QScopedPointer<QConnectionAbstractServer> m_server; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qtremoteobjectglobal.cpp b/src/remoteobjects/qtremoteobjectglobal.cpp new file mode 100644 index 0000000..ce350de --- /dev/null +++ b/src/remoteobjects/qtremoteobjectglobal.cpp @@ -0,0 +1,526 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtremoteobjectglobal.h" + +#include <QMetaObject> +#include <QMetaProperty> +#include "private/qmetaobjectbuilder_p.h" + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(QT_REMOTEOBJECT, "qt.remoteobjects") + +namespace QRemoteObjectPackets { + +QRemoteObjectPacket::~QRemoteObjectPacket(){} + +QRemoteObjectPacket *QRemoteObjectPacket::fromDataStream(QDataStream &in) +{ + QRemoteObjectPacket *packet = Q_NULLPTR; + quint16 type; + in >> type; + switch (type) { + case InitPacket: + packet = new QInitPacket; + if (packet->deserialize(in)) + packet->id = InitPacket; + break; + case InitDynamicPacket: + packet = new QInitDynamicPacket; + if (packet->deserialize(in)) + packet->id = InitDynamicPacket; + break; + case AddObject: + packet = new QAddObjectPacket; + if (packet->deserialize(in)) + packet->id = AddObject; + break; + case RemoveObject: + packet = new QRemoveObjectPacket; + if (packet->deserialize(in)) + packet->id = RemoveObject; + break; + case InvokePacket: + packet = new QInvokePacket; + if (packet->deserialize(in)) + packet->id = InvokePacket; + break; + case PropertyChangePacket: + packet = new QPropertyChangePacket; + if (packet->deserialize(in)) + packet->id = PropertyChangePacket; + break; + case ObjectList: + packet = new QObjectListPacket; + if (packet->deserialize(in)) + packet->id = ObjectList; + break; + default: + qWarning() << "Invalid packet received" << type; + } + return packet; +} + +QByteArray QInitPacketEncoder::serialize() const +{ + DataStreamPacket ds(id); + ds << name; + qint64 postNamePosition = ds.device()->pos(); + ds << quint32(0); + + //Now copy the property data + const QMetaObject *meta = object->metaObject(); + const int propertyOffset = base && base != meta ? base->propertyCount() : meta->propertyOffset(); + const int nParam = meta->propertyCount(); + + ds << quint32(nParam - propertyOffset); //Number of properties + + for (int i = propertyOffset; i < nParam; ++i) { + const QMetaProperty mp = meta->property(i); + ds << mp.name(); + ds << mp.read(object); + } + + //Now go back and set the size of the rest of the data so we can treat is as a QByteArray + ds.device()->seek(postNamePosition); + ds << quint32(ds.array.length() - sizeof(quint32) - postNamePosition); + return ds.finishPacket(); +} + +bool QInitPacketEncoder::deserialize(QDataStream &) +{ + Q_ASSERT(false); //Use QInitPacket::deserialize() + return false; +} + +QByteArray QInitPacket::serialize() const +{ + Q_ASSERT(false); //Use QInitPacketEncoder::serialize() + return QByteArray(); +} + +bool QInitPacket::deserialize(QDataStream& in) +{ + if (in.atEnd()) + return false; + in >> name; + if (name.isEmpty() || name.isNull() || in.atEnd()) + return false; + in >> packetData; + if (packetData.isEmpty() || packetData.isNull()) + return false; + + //Make sure the bytearray holds valid properties + QDataStream validate(packetData); + const int packetLen = packetData.size(); + quint32 nParam, len; + quint8 c; + QVariant tmp; + validate >> nParam; + for (quint32 i = 0; i < nParam; i++) + { + const qint64 pos = validate.device()->pos(); + qint64 bytesLeft = packetLen - pos; + if (bytesLeft < 4) + return false; + validate >> len; + bytesLeft -= 4; + if (bytesLeft < len) + return false; + validate.skipRawData(len-1); + validate >> c; + if (c != 0) + return false; + if (qstrlen(packetData.constData()+pos+4) != len - 1) + return false; + bytesLeft -= len; + if (bytesLeft <= 0) + return false; + validate >> tmp; + if (!tmp.isValid()) + return false; + } + return true; +} + +QByteArray QInitDynamicPacketEncoder::serialize() const +{ + DataStreamPacket ds(id); + ds << name; + qint64 postNamePosition = ds.device()->pos(); + ds << quint32(0); + + //Now copy the property data + const QMetaObject *meta = object->metaObject(); + const int propertyOffset = base && base != meta ? base->propertyCount() : meta->propertyOffset(); + const int nParam = meta->propertyCount(); + const int methodOffset = base && base != meta ? base->methodCount() : meta->methodOffset(); + const int numMethods = meta->methodCount(); + + ds << quint32(numMethods - methodOffset); + for (int i = methodOffset; i < numMethods; ++i) { + const QMetaMethod mm = meta->method(i); + ds << quint32(i - methodOffset); + ds << mm.name(); + ds << mm.methodSignature(); + ds << quint32(mm.methodType()); + if (mm.methodType() == QMetaMethod::Method) + ds << mm.typeName(); + ds << quint32(mm.parameterCount()); + for (int i = 0; i < mm.parameterCount(); ++i) + ds << mm.parameterTypes()[i]; + } + + ds << quint32(nParam - propertyOffset); //Number of properties + + for (int i = propertyOffset; i < nParam; ++i) { + const QMetaProperty mp = meta->property(i); + ds << mp.name(); + ds << mp.typeName(); + if (mp.notifySignalIndex() == -1) + ds << QByteArray(); + else + ds << mp.notifySignal().methodSignature(); + ds << mp.read(object); + } + + //Now go back and set the size of the rest of the data so we can treat is as a QByteArray + ds.device()->seek(postNamePosition); + ds << quint32(ds.array.length() - sizeof(quint32) - postNamePosition); + return ds.finishPacket(); +} + +bool QInitDynamicPacketEncoder::deserialize(QDataStream &) +{ + Q_ASSERT(false); //Use QInitDynamicPacket::deserialize() + return false; +} + +QByteArray QInitDynamicPacket::serialize() const +{ + Q_ASSERT(false); //Use QInitDynamicPacketEncoder::serialize() + return QByteArray(); +} + +bool QInitDynamicPacket::deserialize(QDataStream& in) +{ + if (in.atEnd()) + return false; + in >> name; + if (name.isEmpty() || name.isNull() || in.atEnd()) + return false; + in >> packetData; + if (packetData.isEmpty() || packetData.isNull()) + return false; + + //Make sure the bytearray holds valid properties // TODO maybe really evaluate + return true; + QDataStream validate(packetData); + int packetLen = packetData.size(); + quint32 nParam, len, propLen; + quint8 c; + QVariant tmp; + validate >> nParam; + for (quint32 i = 0; i < nParam; i++) + { + qint64 pos = validate.device()->pos(); + qint64 bytesLeft = packetLen - pos; + if (bytesLeft < 4) + return false; + + //Read property name + validate >> len; + bytesLeft -= 4; + if (bytesLeft < len) + return false; + validate.skipRawData(len-1); + validate >> c; + if (c != 0) + return false; + if (qstrlen(packetData.constData()+pos+4) != len - 1) + return false; + bytesLeft -= len; + if (bytesLeft <= 0) + return false; + + //Read notify name + propLen = len; + validate >> len; + bytesLeft -= 4; + if (bytesLeft < len) + return false; + if (len) { //notify isn't empty + validate.skipRawData(len-1); + validate >> c; + if (c != 0) + return false; + if (qstrlen(packetData.constData()+pos+4+propLen+4) != len - 1) + return false; + bytesLeft -= len; + } + if (bytesLeft <= 0) + return false; + + //Read QVariant value + validate >> tmp; + if (!tmp.isValid()) + return false; + } + return true; +} + +QMetaObject *QInitDynamicPacket::createMetaObject(QMetaObjectBuilder &builder, + QVector<int> &methodTypes, + QVector<QVector<int> > &methodArgumentTypes, + QVector<QPair<QByteArray, QVariant> > *propertyValues) const +{ + quint32 numMethods = 0; + QDataStream ds(packetData); + ds >> numMethods; + methodTypes.clear(); + methodTypes.resize(numMethods); + methodArgumentTypes.clear(); + methodArgumentTypes.resize(numMethods); + for (quint32 i = 0; i < numMethods; ++i) { + quint32 index = 0; + QMetaMethod::MethodType type; + QByteArray name; + QByteArray signature; + QByteArray returnType; + quint32 typeValue; + + ds >> index; + ds >> name; + ds >> signature; + ds >> typeValue; + + type = static_cast<QMetaMethod::MethodType>(typeValue); + methodTypes[i] = type; + if (typeValue == QMetaMethod::Method) + ds >> returnType; + quint32 parameterCount = 0; + ds >> parameterCount; + QByteArray parameterType; + methodArgumentTypes[index].reserve(parameterCount); + for (unsigned int pCount = 0; pCount < parameterCount; ++pCount) { + ds >> parameterType; + methodArgumentTypes[index] << QVariant::nameToType(parameterType.constData()); + } + if (type == QMetaMethod::Signal) + builder.addSignal(signature); + else if (type == QMetaMethod::Slot) + builder.addMethod(signature); + else + builder.addMethod(signature, returnType); + } + + quint32 numProperties = 0; + ds >> numProperties; //Number of properties + + QVector<QPair<QByteArray, QVariant> > &propVal = *propertyValues; + for (unsigned int i = 0; i < numProperties; ++i) { + QByteArray name; + QByteArray typeName; + QByteArray signalName; + ds >> name; + ds >> typeName; + ds >> signalName; + if (signalName.isEmpty()) + builder.addProperty(name, typeName); + else + builder.addProperty(name, typeName, builder.indexOfSignal(signalName)); + QVariant value; + ds >> value; + propVal.append(qMakePair(name, value)); + } + + return builder.toMetaObject(); +} + + +QByteArray QAddObjectPacket::serialize() const +{ + DataStreamPacket ds(id); + ds << name; + ds << isDynamic; + return ds.finishPacket(); +} + +bool QAddObjectPacket::deserialize(QDataStream& in) +{ + in >> name; + in >> isDynamic; + return true; +} + +QByteArray QRemoveObjectPacket::serialize() const +{ + DataStreamPacket ds(id); + ds << name; + return ds.finishPacket(); +} + +bool QRemoveObjectPacket::deserialize(QDataStream& in) +{ + in >> name; + return true; +} + +QByteArray QInvokePacket::serialize() const +{ + DataStreamPacket ds(id); + ds << name; + ds << call; + ds << index; + ds << args; + return ds.finishPacket(); +} + +bool QInvokePacket::deserialize(QDataStream& in) +{ + in >> name; + in >> call; + in >> index; + in >> args; + return true; +} + +QByteArray QPropertyChangePacket::serialize() const +{ + DataStreamPacket ds(id); + ds << name; + ds << propertyName; + ds << value; + return ds.finishPacket(); +} + +bool QPropertyChangePacket::deserialize(QDataStream& in) +{ + in >> name; + in >> propertyName; + in >> value; + return true; +} + +QByteArray QObjectListPacket::serialize() const +{ + DataStreamPacket ds(id); + ds << objects; + return ds.finishPacket(); +} + +bool QObjectListPacket::deserialize(QDataStream& in) +{ + in >> objects; + return true; +} + +} + +namespace QtRemoteObjects { + +void copyStoredProperties(const QObject *src, QObject *dst) +{ + if (!src) { + qCWarning(QT_REMOTEOBJECT) << Q_FUNC_INFO << ": trying to copy from a null QObject"; + return; + } + if (!dst) { + qCWarning(QT_REMOTEOBJECT) << Q_FUNC_INFO << ": trying to copy to a null QObject"; + return; + } + + const QMetaObject * const mof = src->metaObject(); + const QMetaObject * const mot = dst->metaObject(); + if (mof->propertyCount() != mot->propertyCount()) { + qCWarning(QT_REMOTEOBJECT) << Q_FUNC_INFO << ": trying to copy from a different QObject"; + return; + } + + for (int i = 0, end = mof->propertyCount(); i != end; ++i) { + const QMetaProperty mpf = mof->property(i); + if (!mpf.isStored(src)) + continue; + const QMetaProperty mpt = mot->property(i); + mpt.write(dst, mpf.read(src)); + } +} + +void copyStoredProperties(const QObject *src, QDataStream &dst) +{ + if (!src) { + qCWarning(QT_REMOTEOBJECT) << Q_FUNC_INFO << ": trying to copy from a null QObject"; + return; + } + + const QMetaObject * const mof = src->metaObject(); + + for (int i = 0, end = mof->propertyCount(); i != end; ++i) { + const QMetaProperty mpf = mof->property(i); + if (!mpf.isStored(src)) + continue; + dst << mpf.read(src); + } +} + +void copyStoredProperties(QDataStream &src, QObject *dst) +{ + if (!dst) { + qCWarning(QT_REMOTEOBJECT) << Q_FUNC_INFO << ": trying to copy to a null QObject"; + return; + } + + const QMetaObject * const mot = dst->metaObject(); + + for (int i = 0, end = mot->propertyCount(); i != end; ++i) { + const QMetaProperty mpt = mot->property(i); + if (!mpt.isStored(dst)) + continue; + QVariant v; + src >> v; + mpt.write(dst, v); + } +} + +} // namespace QtRemoteObjects + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qtremoteobjectglobal.h b/src/remoteobjects/qtremoteobjectglobal.h new file mode 100644 index 0000000..53442f0 --- /dev/null +++ b/src/remoteobjects/qtremoteobjectglobal.h @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTREMOTEOBJECTGLOBAL_H +#define QTREMOTEOBJECTGLOBAL_H + +#include <QtCore/qglobal.h> +#include <QtCore/QHash> +#include <QtCore/QMap> +#include <QtCore/QPair> +#include <QtCore/QUrl> +#include <QtCore/QVariant> +#include <QtCore/QLoggingCategory> + +QT_BEGIN_NAMESPACE + +typedef QPair<QString, QUrl> QRemoteObjectSourceLocation; +typedef QHash<QString, QUrl> QRemoteObjectSourceLocations; + +Q_DECLARE_METATYPE(QRemoteObjectSourceLocation) +Q_DECLARE_METATYPE(QRemoteObjectSourceLocations) + +#ifndef QT_STATIC +# if defined(QT_BUILD_REMOTEOBJECTS_LIB) +# define Q_REMOTEOBJECTS_EXPORT Q_DECL_EXPORT +# else +# define Q_REMOTEOBJECTS_EXPORT Q_DECL_IMPORT +# endif +#else +# define Q_REMOTEOBJECTS_EXPORT +#endif + +#define QCLASSINFO_REMOTEOBJECT_TYPE "RemoteObject Type" + +class QDataStream; +class QMetaObjectBuilder; + +namespace QRemoteObjectStringLiterals { + +// when QStringLiteral is used with the same string in different functions, +// it creates duplicate static data. Wrapping it in inline functions prevents it. + +inline QString local() { return QStringLiteral("local"); } +inline QString tcp() { return QStringLiteral("tcp"); } + +} + +namespace QRemoteObjectPackets { + +//Helper class for creating a QByteArray from a QRemoteObjectPacket +class DataStreamPacket : public QDataStream +{ +public: + DataStreamPacket(quint16 id) : QDataStream(&array, QIODevice::WriteOnly) + { + *this << quint32(0); + *this << id; + } + QByteArray finishPacket() + { + this->device()->seek(0); + *this << quint32(array.length() - sizeof(quint32)); + return array; + } + QByteArray array; + +private: + Q_DISABLE_COPY(DataStreamPacket) +}; + +class QRemoteObjectPacket +{ +public: + enum QRemoteObjectPacketTypeEnum + { + Invalid = 0, + InitPacket, + InitDynamicPacket, + AddObject, + RemoveObject, + InvokePacket, + PropertyChangePacket, + ObjectList + }; + + QRemoteObjectPacket() { id = AddObject; } + virtual ~QRemoteObjectPacket(); + virtual QByteArray serialize() const = 0; + virtual bool deserialize(QDataStream&) = 0; + static QRemoteObjectPacket* fromDataStream(QDataStream&); + quint16 id; +}; + +class QInitPacketEncoder : public QRemoteObjectPacket +{ +public: + inline QInitPacketEncoder(const QString &_name, const QObject *_object, QMetaObject const *_base = Q_NULLPTR) : + name(_name), object(_object), base(_base) { id = InitPacket; } + QByteArray serialize() const Q_DECL_OVERRIDE; + virtual bool deserialize(QDataStream&) Q_DECL_OVERRIDE; + const QString name; + const QObject *object; + const QMetaObject *base; +private: + QInitPacketEncoder() {} +}; + +class QInitPacket : public QRemoteObjectPacket +{ +public: + inline QInitPacket() : QRemoteObjectPacket() {} + QByteArray serialize() const Q_DECL_OVERRIDE; + virtual bool deserialize(QDataStream&) Q_DECL_OVERRIDE; + QString name; + QByteArray packetData; +}; + +class QInitDynamicPacketEncoder : public QRemoteObjectPacket +{ +public: + QInitDynamicPacketEncoder(const QString &_name, const QObject *_object, QMetaObject const *_base = Q_NULLPTR) : + name(_name), object(_object), base(_base) { id = InitDynamicPacket; } + QByteArray serialize() const Q_DECL_OVERRIDE; + bool deserialize(QDataStream&) Q_DECL_OVERRIDE; + const QString name; + const QObject *object; + const QMetaObject *base; +private: + QInitDynamicPacketEncoder() {} +}; + +class QInitDynamicPacket : public QRemoteObjectPacket +{ +public: + inline QInitDynamicPacket() : QRemoteObjectPacket() {} + QByteArray serialize() const Q_DECL_OVERRIDE; + bool deserialize(QDataStream&) Q_DECL_OVERRIDE; + QMetaObject *createMetaObject(QMetaObjectBuilder &builder, + QVector<int> &methodTypes, + QVector<QVector<int> > &methodArgumentTypes, + QVector<QPair<QByteArray, QVariant> > *propertyValues = 0) const; + QString name; + QByteArray packetData; +}; + +class QAddObjectPacket : public QRemoteObjectPacket +{ +public: + inline QAddObjectPacket() : QRemoteObjectPacket(), + isDynamic(false) {} + inline QAddObjectPacket(const QString &_name, bool _isDynamic) : + name(_name), isDynamic(_isDynamic) { id = AddObject; } + virtual QByteArray serialize() const Q_DECL_OVERRIDE; + virtual bool deserialize(QDataStream&) Q_DECL_OVERRIDE; + QString name; + bool isDynamic; +}; + +class QRemoveObjectPacket : public QRemoteObjectPacket +{ +public: + inline QRemoveObjectPacket() : QRemoteObjectPacket() {} + inline QRemoveObjectPacket(const QString &_name) : + name(_name) { id = RemoveObject; } + virtual QByteArray serialize() const Q_DECL_OVERRIDE; + virtual bool deserialize(QDataStream&) Q_DECL_OVERRIDE; + QString name; +}; + +class QInvokePacket : public QRemoteObjectPacket +{ +public: + inline QInvokePacket() : QRemoteObjectPacket(), + call(-1), index(-1) {} + inline QInvokePacket(const QString &_name, int _call, int _index, QVariantList _args) : + name(_name), call(_call), index(_index), args(_args) { id = InvokePacket; } + virtual QByteArray serialize() const Q_DECL_OVERRIDE; + virtual bool deserialize(QDataStream&) Q_DECL_OVERRIDE; + QString name; + int call; + int index; + QVariantList args; +}; + +class QPropertyChangePacket : public QRemoteObjectPacket +{ +public: + inline QPropertyChangePacket() : QRemoteObjectPacket() {} + inline QPropertyChangePacket(const QString &_name, const char *_propertyNameChar, const QVariant &_value) : + name(_name), propertyName(_propertyNameChar), value(_value) { id = PropertyChangePacket; } + virtual QByteArray serialize() const Q_DECL_OVERRIDE; + virtual bool deserialize(QDataStream&) Q_DECL_OVERRIDE; + QString name; + QByteArray propertyName; + QVariant value; +}; + +class QObjectListPacket : public QRemoteObjectPacket +{ +public: + inline QObjectListPacket() : QRemoteObjectPacket() {} + inline QObjectListPacket(const QStringList &_objects) : + objects(_objects) { id = ObjectList; } + virtual QByteArray serialize() const Q_DECL_OVERRIDE; + virtual bool deserialize(QDataStream&) Q_DECL_OVERRIDE; + QStringList objects; +}; + +} // namespace QRemoteObjectPackets + +namespace QtRemoteObjects { + +Q_REMOTEOBJECTS_EXPORT void copyStoredProperties(const QObject *src, QObject *dst); +Q_REMOTEOBJECTS_EXPORT void copyStoredProperties(const QObject *src, QDataStream &dst); +Q_REMOTEOBJECTS_EXPORT void copyStoredProperties(QDataStream &src, QObject *dst); + +} + +Q_DECLARE_LOGGING_CATEGORY(QT_REMOTEOBJECT) + +QT_END_NAMESPACE + +#endif // QTREMOTEOBJECTSGLOBAL_H diff --git a/src/remoteobjects/remoteobjects.pro b/src/remoteobjects/remoteobjects.pro new file mode 100644 index 0000000..9e967c3 --- /dev/null +++ b/src/remoteobjects/remoteobjects.pro @@ -0,0 +1,51 @@ +TARGET = QtRemoteObjects +MODULE = remoteobjects +MODULE_CONFIG = remoteobjects_repc +QT += network core-private +QT -= gui + +QMAKE_DOCS = $$PWD/doc/qtremoteobjects.qdocconf + +load(qt_module) + +DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_BYTEARRAY QT_NO_URL_CAST_FROM_STRING + +INCLUDEPATH += $$PWD + +PUBLIC_HEADERS += \ + $$PWD/qremoteobjectdynamicreplica.h \ + $$PWD/qremoteobjectsource.h \ + $$PWD/qremoteobjectreplica.h \ + $$PWD/qremoteobjectnode.h \ + $$PWD/qtremoteobjectglobal.h \ + $$PWD/qremoteobjectregistry.h + +PRIVATE_HEADERS += \ + $$PWD/qconnectionabstractfactory_p.h \ + $$PWD/qconnectionabstractserver_p.h \ + $$PWD/qconnectionclientfactory_p.h \ + $$PWD/qconnectionserverfactory_p.h \ + $$PWD/qremoteobjectsourceio_p.h \ + $$PWD/qremoteobjectsource_p.h \ + $$PWD/qregistrysource_p.h \ + $$PWD/qremoteobjectnode_p.h \ + $$PWD/qremoteobjectreplica_p.h + +HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS + +SOURCES += \ + $$PWD/qconnectionabstractserver.cpp \ + $$PWD/qconnectionclientfactory.cpp \ + $$PWD/qconnectionserverfactory.cpp \ + $$PWD/qremoteobjectdynamicreplica.cpp \ + $$PWD/qremoteobjectsource.cpp \ + $$PWD/qremoteobjectsourceio.cpp \ + $$PWD/qremoteobjectregistry.cpp \ + $$PWD/qregistrysource.cpp \ + $$PWD/qremoteobjectreplica.cpp \ + $$PWD/qremoteobjectnode.cpp \ + $$PWD/qtremoteobjectglobal.cpp + +DEFINES += QT_BUILD_REMOTEOBJECTS_LIB + +contains(QT_CONFIG, c++11): CONFIG += c++11 diff --git a/src/src.pro b/src/src.pro new file mode 100644 index 0000000..f16d105 --- /dev/null +++ b/src/src.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS = remoteobjects diff --git a/sync.profile b/sync.profile new file mode 100644 index 0000000..0f6840a --- /dev/null +++ b/sync.profile @@ -0,0 +1,18 @@ +%modules = ( # path to module name map + "QtRemoteObjects" => "$basedir/src/remoteobjects", +); +#%moduleheaders = ( # restrict the module headers to those found in relative path +# +#); +%deprecatedheaders = ( +); +# Module dependencies. +# Every module that is required to build this module should have one entry. +# Each of the module version specifiers can take one of the following values: +# - A specific Git revision. +# - any git symbolic ref resolvable from the module's repository (e.g. "refs/heads/master" to track master branch) +# - an empty string to use the same branch under test (dependencies will become "refs/heads/master" if we are in the master branch) +# +%dependencies = ( + "qtbase" => "", +); diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro new file mode 100644 index 0000000..80475ba --- /dev/null +++ b/tests/auto/auto.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += repc integration cmake diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt new file mode 100644 index 0000000..62fc89a --- /dev/null +++ b/tests/auto/cmake/CMakeLists.txt @@ -0,0 +1,12 @@ + +cmake_minimum_required(VERSION 2.8) + +project(qmake_cmake_files) + +enable_testing() + +find_package(Qt5Core REQUIRED) + +include("${_Qt5CTestMacros}") + +expect_pass(test_qremoteobjects_module) diff --git a/tests/auto/cmake/cmake.pro b/tests/auto/cmake/cmake.pro new file mode 100644 index 0000000..bf2dbcb --- /dev/null +++ b/tests/auto/cmake/cmake.pro @@ -0,0 +1,5 @@ + +# Cause make to do nothing. +TEMPLATE = subdirs + +CONFIG += ctest_testcase diff --git a/tests/auto/cmake/test_qremoteobjects_module/CMakeLists.txt b/tests/auto/cmake/test_qremoteobjects_module/CMakeLists.txt new file mode 100644 index 0000000..e4b3b0e --- /dev/null +++ b/tests/auto/cmake/test_qremoteobjects_module/CMakeLists.txt @@ -0,0 +1,32 @@ + +cmake_minimum_required(VERSION 2.8) + +project(test_qremoteobjects_module) + +find_package(Qt5Core REQUIRED) +find_package(Qt5Gui REQUIRED) +find_package(Qt5Network REQUIRED) + +find_package(Qt5RemoteObjects REQUIRED) + +include_directories( + ${Qt5RemoteObjects_INCLUDE_DIRS} + ${Qt5Core_INCLUDE_DIRS} + ${Qt5Network_INCLUDE_DIRS} +) + +add_definitions( + ${Qt5RemoteObjects_DEFINITIONS} + ${QtCore_DEFINITIONS} + ${Qt5Network_DEFINITIONS} +) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Core_EXECUTABLE_COMPILE_FLAGS} ${Qt5Network_EXECUTABLE_COMPILE_FLAGS} ${Qt5Replicant_EXECUTABLE_COMPILE_FLAGS}") + +add_executable(mainapp main.cpp) + +target_link_libraries(mainapp + ${Qt5RemoteObjects_LIBRARIES} + ${Qt5Core_LIBRARIES} + ${Qt5Network_LIBRARIES} +) diff --git a/tests/auto/cmake/test_qremoteobjects_module/main.cpp b/tests/auto/cmake/test_qremoteobjects_module/main.cpp new file mode 100644 index 0000000..0887d07 --- /dev/null +++ b/tests/auto/cmake/test_qremoteobjects_module/main.cpp @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QRemoteObjectNode> + +int main(int argc, char **argv) +{ + QRemoteObjectNode node; + + return 0; +} diff --git a/tests/auto/integration/.gitignore b/tests/auto/integration/.gitignore new file mode 100644 index 0000000..151e5e1 --- /dev/null +++ b/tests/auto/integration/.gitignore @@ -0,0 +1 @@ +/integration diff --git a/tests/auto/integration/Engine.cpp b/tests/auto/integration/Engine.cpp new file mode 100644 index 0000000..5beaf5e --- /dev/null +++ b/tests/auto/integration/Engine.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "Engine.h" + +Engine::Engine(QObject *parent) : + EngineSource(parent) +{ + setRpm(0); +} + +Engine::~Engine() +{ +} diff --git a/tests/auto/integration/Engine.h b/tests/auto/integration/Engine.h new file mode 100644 index 0000000..5b068d1 --- /dev/null +++ b/tests/auto/integration/Engine.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TESTS_ENGINE_H +#define TESTS_ENGINE_H + +#include "rep_engine_source.h" + +class Engine : public EngineSource +{ + Q_OBJECT +public: + Engine(QObject *parent=Q_NULLPTR); + virtual ~Engine(); + +private: + int speed; +}; + +#endif diff --git a/tests/auto/integration/RemoteObjectTest.h b/tests/auto/integration/RemoteObjectTest.h new file mode 100644 index 0000000..f82583c --- /dev/null +++ b/tests/auto/integration/RemoteObjectTest.h @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TESTS_REMOTEOBJECTTEST_H +#define TESTS_REMOTEOBJECTTEST_H + +#include <QtTest/QtTest> +#include <QMetaType> +#include <qremoteobjectreplica.h> +#include <QRemoteObjectNode> +#include "Engine.h" +#include "Speedometer.h" +#include "rep_engine_replica.h" +#include "rep_speedometer_replica.h" +#include "rep_localdatacenter_source.h" +#include "rep_tcpdatacenter_source.h" +#include "rep_localdatacenter_replica.h" +#include "rep_tcpdatacenter_replica.h" + +//DUMMY impl for variant comparison +bool operator<(const QVector<int> &lhs, const QVector<int> &rhs) +{ + return lhs.size() < rhs.size(); +} + +class RemoteObjectTest: public QObject +{ + Q_OBJECT + QRemoteObjectNode m_client; + QRemoteObjectNode m_registryClient; + QRemoteObjectNode m_basicServer; + QRemoteObjectNode m_localCentreServer; + QRemoteObjectNode m_tcpCentreServer; + QRemoteObjectNode m_registryServer; +private slots: + + void initTestCase() { + QLoggingCategory::setFilterRules("*.debug=true\n" + "qt.remoteobjects.debug=false\n" + "qt.remoteobjects.warning=false"); + //Setup registry + //Registry needs to be created first until we get the retry mechanism implemented + m_registryServer = QRemoteObjectNode::createRegistryHostNode(); + + m_client = QRemoteObjectNode(); + m_registryClient = QRemoteObjectNode::createNodeConnectedToRegistry(); + //m_client.setObjectName("DirectTestClient"); + //m_registryClient.setObjectName("RegistryTestClient"); + + m_basicServer = QRemoteObjectNode::createHostNode(QUrl("tcp://localhost:9999")); + + engine.reset(new Engine); + speedometer.reset(new Speedometer); + m_basicServer.enableRemoting(engine.data()); + m_basicServer.enableRemoting(speedometer.data()); + + m_client.connect(QUrl("tcp://localhost:9999")); + + //setup servers + qRegisterMetaType<QVector<int> >(); + QMetaType::registerComparators<QVector<int> >(); + qRegisterMetaTypeStreamOperators<QVector<int> >(); + m_localCentreServer = QRemoteObjectNode::createHostNodeConnectedToRegistry(); + dataCenterLocal.reset(new LocalDataCenterSource); + dataCenterLocal->setData1(5); + dataCenterLocal->setData2(5.0); + dataCenterLocal->setData3(QStringLiteral("local")); + dataCenterLocal->setData4(QVector<int>() << 1 << 2 << 3 << 4 << 5); + m_localCentreServer.enableRemoting(dataCenterLocal.data()); + + m_tcpCentreServer = QRemoteObjectNode::createHostNodeConnectedToRegistry(QUrl("tcp://localhost:19999")); + dataCenterTcp.reset(new TcpDataCenterSource); + dataCenterTcp->setData1(5); + dataCenterTcp->setData2(5.0); + dataCenterTcp->setData3(QStringLiteral("tcp")); + dataCenterTcp->setData4(QVector<int>() << 1 << 2 << 3 << 4 << 5); + m_tcpCentreServer.enableRemoting(dataCenterTcp.data()); + + //Setup the client + //QVERIFY(m_registryClient.connect( QStringLiteral("local:replica"))); + } + + void RegistryTest() { + QSharedPointer<TcpDataCenterReplica> tcpCentre(m_registryClient.acquire<TcpDataCenterReplica>()); + QSharedPointer<LocalDataCenterReplica> localCentre(m_registryClient.acquire<LocalDataCenterReplica>()); + tcpCentre->waitForSource(); + localCentre->waitForSource(); + QCOMPARE(m_registryClient.registry()->sourceLocations(), m_registryServer.registry()->sourceLocations()); + QVERIFY(localCentre->isInitialized()); + QVERIFY(tcpCentre->isInitialized()); + + QCOMPARE(tcpCentre->data1(), 5 ); + QCOMPARE(tcpCentre->data2(), 5.0); + QCOMPARE(tcpCentre->data3(), QStringLiteral("tcp")); + QCOMPARE(tcpCentre->data4(), QVector<int>() << 1 << 2 << 3 << 4 << 5); + + QCOMPARE(localCentre->data1(), 5); + QCOMPARE(localCentre->data2(), 5.0); + QCOMPARE(localCentre->data3(), QStringLiteral("local")); + QCOMPARE(localCentre->data4(), QVector<int>() << 1 << 2 << 3 << 4 << 5); + + } + + void basicTest() { + engine->setRpm(1234); + + QSharedPointer<EngineReplica> engine_r(m_client.acquire<EngineReplica>()); + engine_r->waitForSource(); + QCOMPARE(engine_r->rpm(), 1234); + } + + void sequentialReplicaTest() { + engine->setRpm(3456); + + QSharedPointer<EngineReplica> engine_r(m_client.acquire<EngineReplica>()); + engine_r->waitForSource(); + QCOMPARE(engine_r->rpm(), 3456); + + engine_r = QSharedPointer<EngineReplica>(m_client.acquire< EngineReplica >()); + engine_r->waitForSource(); + QCOMPARE(engine_r->rpm(), 3456); + } + + void doubleReplicaTest() { + QSharedPointer<EngineReplica> engine_r1(m_client.acquire< EngineReplica >()); + QSharedPointer<EngineReplica> engine_r2(m_client.acquire< EngineReplica >()); + engine->setRpm(3412); + + engine_r1->waitForSource(); + engine_r2->waitForSource(); + + QCOMPARE(engine_r1->rpm(), 3412); + QCOMPARE(engine_r2->rpm(), 3412); + } + + void twoReplicaTest() { + engine->setRpm(1234); + speedometer->setMph(70); + + QSharedPointer<EngineReplica> engine_r(m_client.acquire<EngineReplica>()); + engine_r->waitForSource(); + QSharedPointer<SpeedometerReplica> speedometer_r(m_client.acquire<SpeedometerReplica>()); + speedometer_r->waitForSource(); + + QCOMPARE(engine_r->rpm(), 1234); + QCOMPARE(speedometer_r->mph(), 70); + } + + void dynamicReplicaTest() { + QRemoteObjectDynamicReplica *rep1 = m_registryClient.acquire("TcpDataCenter"); + QRemoteObjectDynamicReplica *rep2 = m_registryClient.acquire("TcpDataCenter"); + QRemoteObjectDynamicReplica *rep3 = m_registryClient.acquire("LocalDataCenter"); + rep1->waitForSource(); + rep2->waitForSource(); + rep3->waitForSource(); + const QMetaObject *metaTcpRep1 = rep1->metaObject(); + const QMetaObject *metaLocalRep1 = rep3->metaObject(); + const QMetaObject *metaTcpSource = dataCenterTcp->metaObject(); + const QMetaObject *metaLocalSource = dataCenterLocal->metaObject(); + QVERIFY(rep1->isInitialized()); + QVERIFY(rep2->isInitialized()); + QVERIFY(rep3->isInitialized()); + + for (int i = 0; i < metaTcpRep1->propertyCount(); ++i) + { + const QMetaProperty propLhs = metaTcpRep1->property(i); + const QMetaProperty propRhs = metaTcpSource->property(metaTcpSource->indexOfProperty(propLhs.name())); + QCOMPARE(propLhs.notifySignalIndex(), propRhs.notifySignalIndex()); + QCOMPARE(propLhs.read(rep1), propRhs.read(dataCenterTcp.data())); + QCOMPARE(propLhs.read(rep2), propRhs.read(rep1)); + } + for (int i = 0; i < metaLocalRep1->propertyCount(); ++i ) + { + const QMetaProperty propLhs = metaLocalRep1->property(i); + const QMetaProperty propRhs = metaLocalSource->property(metaTcpSource->indexOfProperty(propLhs.name())); + QCOMPARE(propLhs.notifySignalIndex(), propRhs.notifySignalIndex()); + QCOMPARE(propLhs.read(rep3), propRhs.read(dataCenterLocal.data())); + } + + } + +private: + QScopedPointer<Engine> engine; + QScopedPointer<Speedometer> speedometer; + QScopedPointer<TcpDataCenterSource> dataCenterTcp; + QScopedPointer<LocalDataCenterSource> dataCenterLocal; +}; + +QTEST_MAIN(RemoteObjectTest) + +#endif diff --git a/tests/auto/integration/Speedometer.cpp b/tests/auto/integration/Speedometer.cpp new file mode 100644 index 0000000..50a35af --- /dev/null +++ b/tests/auto/integration/Speedometer.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "Speedometer.h" + +Speedometer::Speedometer(QObject *parent) : + SpeedometerSource(parent) +{ +} + +Speedometer::~Speedometer() +{ +} diff --git a/tests/auto/integration/Speedometer.h b/tests/auto/integration/Speedometer.h new file mode 100644 index 0000000..71c8645 --- /dev/null +++ b/tests/auto/integration/Speedometer.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TESTS_SPEEDOMETER_H +#define TESTS_SPEEDOMETER_H + +#include "rep_speedometer_source.h" + +class Speedometer : public SpeedometerSource +{ + Q_OBJECT +public: + Speedometer(QObject *parent=Q_NULLPTR); + virtual ~Speedometer(); + +private: + int speed; +}; + +#endif diff --git a/tests/auto/integration/engine.rep b/tests/auto/integration/engine.rep new file mode 100644 index 0000000..4bdba22 --- /dev/null +++ b/tests/auto/integration/engine.rep @@ -0,0 +1,5 @@ +#include <QtCore> +class Engine +{ + PROP(int rpm); +}; diff --git a/tests/auto/integration/integration.pro b/tests/auto/integration/integration.pro new file mode 100644 index 0000000..977b3f8 --- /dev/null +++ b/tests/auto/integration/integration.pro @@ -0,0 +1,24 @@ +QT += network testlib + +QT -= gui + +OTHER_FILES = engine.rep \ + speedometer.rep \ + ../repfiles/localdatacenter.rep \ + ../repfiles/tcpdatacenter.rep + +REPC_SOURCE += $$OTHER_FILES +REPC_REPLICA += $$OTHER_FILES +QT += remoteobjects + +CONFIG += testcase + +HEADERS += $$PWD/RemoteObjectTest.h \ + $$PWD/Engine.h \ + $$PWD/Speedometer.h + +SOURCES += $$PWD/Engine.cpp \ + $$PWD/Speedometer.cpp + +contains(QT_CONFIG, c++11): CONFIG += c++11 + diff --git a/tests/auto/integration/speedometer.rep b/tests/auto/integration/speedometer.rep new file mode 100644 index 0000000..291adb3 --- /dev/null +++ b/tests/auto/integration/speedometer.rep @@ -0,0 +1,5 @@ +#include <QtCore> +class Speedometer +{ + PROP(int mph); +}; diff --git a/tests/auto/repc/pods/.gitignore b/tests/auto/repc/pods/.gitignore new file mode 100644 index 0000000..ccb20c9 --- /dev/null +++ b/tests/auto/repc/pods/.gitignore @@ -0,0 +1 @@ +tst_pods diff --git a/tests/auto/repc/pods/pods.pro b/tests/auto/repc/pods/pods.pro new file mode 100644 index 0000000..db7188c --- /dev/null +++ b/tests/auto/repc/pods/pods.pro @@ -0,0 +1,13 @@ +CONFIG += testcase parallel_test +TARGET = tst_pods +QT += testlib remoteobjects +QT -= gui + +SOURCES += tst_pods.cpp + +REP_FILES = pods.rep + +#REPC_SOURCE = $$REP_FILES # can't have both source and replica in the same process if PODs are involved... +REPC_REPLICA = $$REP_FILES + +contains(QT_CONFIG, c++11):CONFIG += c++11 diff --git a/tests/auto/repc/pods/pods.rep b/tests/auto/repc/pods/pods.rep new file mode 100644 index 0000000..da9a0ca --- /dev/null +++ b/tests/auto/repc/pods/pods.rep @@ -0,0 +1,6 @@ +#include <QString> + +POD PodI(int i) +POD PodF(float f) +POD PodS(QString s) +POD PodIFS(int i, float f, QString s) diff --git a/tests/auto/repc/pods/tst_pods.cpp b/tests/auto/repc/pods/tst_pods.cpp new file mode 100644 index 0000000..fc3112a --- /dev/null +++ b/tests/auto/repc/pods/tst_pods.cpp @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "rep_pods_replica.h" + +#include <QTest> + +#include <QByteArray> +#include <QDataStream> + +class tst_Pods : public QObject { + Q_OBJECT + +private Q_SLOTS: + void testConstructors(); + void testParent(); + void testMarshaling(); +}; + + +void tst_Pods::testConstructors() +{ + PodI pi1; + QCOMPARE(pi1.i(), 0); + + PodI pi2(1); + QCOMPARE(pi2.i(), 1); + + PodI pi3(pi2); + QCOMPARE(pi3.i(), pi2.i()); + + PodI pi4(static_cast<QObject*>(Q_NULLPTR)); + QCOMPARE(pi4.i(), 0); + + PodI pi5(1, static_cast<QObject*>(Q_NULLPTR)); + QCOMPARE(pi5.i(), 1); +} + +void tst_Pods::testParent() +{ + PodI pi; + QVERIFY(!pi.parent()); +} + +void tst_Pods::testMarshaling() +{ + QByteArray ba; + QDataStream ds(&ba, QIODevice::ReadWrite); + + { + PodI i1(1), i2(2), i3(3), iDeadBeef(0xdeadbeef); + Q_SET_OBJECT_NAME(i1); + Q_SET_OBJECT_NAME(i2); + Q_SET_OBJECT_NAME(i3); + Q_SET_OBJECT_NAME(iDeadBeef); + ds << i1 << i2 << i3 << iDeadBeef; + } + + ds.device()->seek(0); + + { + PodI i1, i2, i3, iDeadBeef; + ds >> i1 >> i2 >> i3 >> iDeadBeef; + + QCOMPARE(i1.objectName(), QLatin1String("i1")); + QCOMPARE(i2.objectName(), QLatin1String("i2")); + QCOMPARE(i3.objectName(), QLatin1String("i3")); + QCOMPARE(iDeadBeef.objectName(), QLatin1String("iDeadBeef")); + + QCOMPARE(i1.i(), 1); + QCOMPARE(i2.i(), 2); + QCOMPARE(i3.i(), 3); + QCOMPARE(iDeadBeef.i(), int(0xdeadbeef)); + } +} + +QTEST_APPLESS_MAIN(tst_Pods) + +#include "tst_pods.moc" diff --git a/tests/auto/repc/repc.pro b/tests/auto/repc/repc.pro new file mode 100644 index 0000000..b0bb9e1 --- /dev/null +++ b/tests/auto/repc/repc.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += pods diff --git a/tests/auto/repfiles/localdatacenter.rep b/tests/auto/repfiles/localdatacenter.rep new file mode 100644 index 0000000..6f0f7c9 --- /dev/null +++ b/tests/auto/repfiles/localdatacenter.rep @@ -0,0 +1,8 @@ +#include <QtCore> +class LocalDataCenter +{ + PROP(int data1); + PROP(float data2); + PROP(QString data3); + PROP(QVector<int> data4); +}; diff --git a/tests/auto/repfiles/tcpdatacenter.rep b/tests/auto/repfiles/tcpdatacenter.rep new file mode 100644 index 0000000..bfd26c2 --- /dev/null +++ b/tests/auto/repfiles/tcpdatacenter.rep @@ -0,0 +1,8 @@ +#include <QtCore> +class TcpDataCenter +{ + PROP(int data1); + PROP(float data2); + PROP(QString data3); + PROP(QVector<int> data4); +}; diff --git a/tests/tests.pro b/tests/tests.pro new file mode 100644 index 0000000..568bdf7 --- /dev/null +++ b/tests/tests.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +CONFIG += debug_and_release +SUBDIRS = auto diff --git a/tools/repc/RepCodeGenerator.cpp b/tools/repc/RepCodeGenerator.cpp new file mode 100644 index 0000000..0dbcb44 --- /dev/null +++ b/tools/repc/RepCodeGenerator.cpp @@ -0,0 +1,460 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "RepCodeGenerator.h" + +#include "RepParser.h" + +#ifndef QT_BOOTSTRAPPED +# define HAVE_QSAVEFILE +#endif +#ifdef HAVE_QSAVEFILE +# include <QSaveFile> +#else +# include <QFile> +#endif +#include <QTextStream> + +template <typename C> +static int accumulatedSizeOfNames(const C &c) +{ + int result = 0; + foreach (const typename C::value_type &e, c) + result += e.name.size(); + return result; +} + +template <typename C> +static int accumulatedSizeOfTypes(const C &c) +{ + int result = 0; + foreach (const typename C::value_type &e, c) + result += e.type.size(); + return result; +} + +static QString cap(QString name) +{ + if (!name.isEmpty()) + name[0] = name[0].toUpper(); + return name; +} + +RepCodeGenerator::RepCodeGenerator(const QString &fileName) + : m_fileName(fileName) +{ +} + +bool RepCodeGenerator::generate(const AST &ast, Mode mode) +{ +#ifdef HAVE_QSAVEFILE + QSaveFile file(m_fileName); +#else + QFile file(m_fileName); +#endif + if (!file.open(QIODevice::WriteOnly)) + return false; + + QTextStream stream(&file); + + QStringList out; + + generateHeader(mode, stream, ast); + foreach (const POD &pod, ast.pods) + generatePOD(stream, pod); + const QString podMetaTypeRegistrationCode = generateMetaTypeRegistrationForPODs(ast.pods); + foreach (const ASTClass &astClass, ast.classes) + generateClass(mode, out, astClass, podMetaTypeRegistrationCode); + + stream << out.join("\n"); +#ifdef HAVE_QSAVEFILE + file.commit(); +#endif + return true; +} + +void RepCodeGenerator::generateHeader(Mode mode, QTextStream &out, const AST &ast) +{ + out << "// This is an autogenerated file.\n" + "// Do not edit this file, any changes made will be lost the next time it is generated.\n" + "\n" + "#include <QObject>\n" + "#include <QVariantList>\n" + "#include <QMetaProperty>\n" + "\n" + "#include <QRemoteObjectNode>\n" + ; + if (mode == REPLICA) + out << "#include <qremoteobjectreplica.h>\n"; + else + out << "#include <QRemoteObjectSource>\n"; + out << "\n"; + + out << ast.includes.join(QLatin1Char('\n')); + out << "\n"; +} + +static QString formatTemplateStringArgTypeNameCapitalizedName(int numberOfTypeOccurrences, int numberOfNameOccurrences, + QString templateString, const POD &pod) +{ + QString out; + const int LengthOfPlaceholderText = 2; + Q_ASSERT(templateString.count(QRegExp("%\\d")) == numberOfNameOccurrences + numberOfTypeOccurrences); + const int expectedOutSize + = numberOfNameOccurrences * accumulatedSizeOfNames(pod.attributes) + + numberOfTypeOccurrences * accumulatedSizeOfTypes(pod.attributes) + + pod.attributes.size() * (templateString.size() - (numberOfNameOccurrences + numberOfTypeOccurrences) * LengthOfPlaceholderText); + out.reserve(expectedOutSize); + foreach (const PODAttribute &a, pod.attributes) + out += templateString.arg(a.type, a.name, cap(a.name)); + return out; +} + +QString RepCodeGenerator::formatQPropertyDeclarations(const POD &pod) +{ + return formatTemplateStringArgTypeNameCapitalizedName(1, 4, QStringLiteral(" Q_PROPERTY(%1 %2 READ %2 WRITE set%3 NOTIFY on%3Changed)\n"), pod); +} + +QString RepCodeGenerator::formatConstructors(const POD &pod) +{ + QString initializerString = QString("QObject(parent)"); + QString defaultInitializerString = initializerString; + QString argString; + foreach (const PODAttribute &a, pod.attributes) { + initializerString += QString(", _%1(%1)").arg(a.name); + defaultInitializerString += QString(", _%1()").arg(a.name); + argString += QString("%1 %2, ").arg(a.type, a.name); + } + argString.chop(2); + + return QString(" explicit %1(QObject *parent = Q_NULLPTR) : %2 {}\n" + " explicit %1(%3, QObject *parent = Q_NULLPTR) : %4 {}\n") + .arg(pod.name, defaultInitializerString, argString, initializerString); +} + +QString RepCodeGenerator::formatCopyConstructor(const POD &pod) +{ + return " " + pod.name + "(const " + pod.name + "& other)\n" + " : QObject()\n" + " {\n" + " QtRemoteObjects::copyStoredProperties(&other, this);\n" + " }\n" + "\n" + ; +} + +QString RepCodeGenerator::formatCopyAssignmentOperator(const POD &pod) +{ + return " " + pod.name + " &operator=(const " + pod.name + " &other)\n" + " {\n" + " if (this != &other)\n" + " QtRemoteObjects::copyStoredProperties(&other, this);\n" + " return *this;\n" + " }\n" + "\n" + ; +} + +QString RepCodeGenerator::formatPropertyGettersAndSetters(const POD &pod) +{ + // MSVC doesn't like adjacent string literal concatenation within QStringLiteral, so keep it in one line: + QString templateString + = QStringLiteral(" %1 %2() const { return _%2; }\n void set%3(%1 %2) { if (%2 != _%2) { _%2 = %2; Q_EMIT on%3Changed(); } }\n"); + return formatTemplateStringArgTypeNameCapitalizedName(2, 9, qMove(templateString), pod); +} + +QString RepCodeGenerator::formatSignals(const POD &pod) +{ + QString out; + const QString prefix = QStringLiteral(" void on"); + const QString suffix = QStringLiteral("Changed();\n"); + const int expectedOutSize + = accumulatedSizeOfNames(pod.attributes) + + pod.attributes.size() * (prefix.size() + suffix.size()); + out.reserve(expectedOutSize); + foreach (const PODAttribute &a, pod.attributes) { + out += prefix; + out += cap(a.name); + out += suffix; + } + Q_ASSERT(out.size() == expectedOutSize); + return out; +} + +QString RepCodeGenerator::formatDataMembers(const POD &pod) +{ + QString out; + const QString prefix = QStringLiteral(" "); + const QString infix = QStringLiteral(" _"); + const QString suffix = QStringLiteral(";\n"); + const int expectedOutSize + = accumulatedSizeOfNames(pod.attributes) + + accumulatedSizeOfTypes(pod.attributes) + + pod.attributes.size() * (prefix.size() + infix.size() + suffix.size()); + out.reserve(expectedOutSize); + foreach (const PODAttribute &a, pod.attributes) { + out += prefix; + out += a.type; + out += infix; + out += a.name; + out += suffix; + } + Q_ASSERT(out.size() == expectedOutSize); + return out; +} + +QString RepCodeGenerator::formatMarshalingOperators(const POD &pod) +{ + return "inline QDataStream &operator<<(QDataStream &ds, const " + pod.name + " &obj) {\n" + " QtRemoteObjects::copyStoredProperties(&obj, ds);\n" + " return ds;\n" + "}\n" + "\n" + "inline QDataStream &operator>>(QDataStream &ds, " + pod.name + " &obj) {\n" + " QtRemoteObjects::copyStoredProperties(ds, &obj);\n" + " return ds;\n" + "}\n" + ; +} + +void RepCodeGenerator::generatePOD(QTextStream &out, const POD &pod) +{ + out << "class " << pod.name << " : public QObject\n" + "{\n" + " Q_OBJECT\n" + << formatQPropertyDeclarations(pod) + << "public:\n" + << formatConstructors(pod) + << formatCopyConstructor(pod) + << formatCopyAssignmentOperator(pod) + << formatPropertyGettersAndSetters(pod) + << "Q_SIGNALS:\n" + << formatSignals(pod) + << "private:\n" + << formatDataMembers(pod) + << "};\n" + "\n" + << formatMarshalingOperators(pod) + << "\n" + "Q_DECLARE_METATYPE(" << pod.name << ")\n" + "\n" + ; +} + +QString RepCodeGenerator::generateMetaTypeRegistrationForPODs(const QVector<POD> &pods) +{ + QString out; + const QString qRegisterMetaType = QStringLiteral(" qRegisterMetaType<"); + const QString qRegisterMetaTypeStreamOperators = QStringLiteral(" qRegisterMetaTypeStreamOperators<"); + const QString lineEnding = QStringLiteral(">();\n"); + const int expectedOutSize + = 2 * accumulatedSizeOfNames(pods) + + pods.size() * (qRegisterMetaType.size() + qRegisterMetaTypeStreamOperators.size() + 2 * lineEnding.size()); + out.reserve(expectedOutSize); + foreach (const POD &pod, pods) { + out += qRegisterMetaType; + out += pod.name; + out += lineEnding; + + out += qRegisterMetaTypeStreamOperators; + out += pod.name; + out += lineEnding; + } + Q_ASSERT(expectedOutSize == out.size()); + return out; +} + +void RepCodeGenerator::generateClass(Mode mode, QStringList &out, const ASTClass &astClass, const QString &podMetaTypeRegistrationCode) +{ + const QString className = (astClass.name() + (mode == REPLICA ? "Replica" : "Source")); + if (mode == REPLICA) + out << QString("class %1 : public QRemoteObjectReplica").arg(className); + else + out << QString("class %1 : public QRemoteObjectSource").arg(className); + + out << "{"; + out << " Q_OBJECT"; + out << QString(" Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_TYPE, \"%1\")").arg(astClass.name()); + out << QString(" friend class QRemoteObjectNode;"); + if (mode == SOURCE) + out <<"public:"; + else + out <<"private:"; + + if (mode == REPLICA) { + out << QString(" %1() : QRemoteObjectReplica() {}").arg(className); + out << QString(" void initialize()"); + } else { + out << QString(" %1(QObject *parent = Q_NULLPTR) : QRemoteObjectSource(parent)").arg(className); + } + + out << " {"; + + if (!podMetaTypeRegistrationCode.isEmpty()) + out << podMetaTypeRegistrationCode; + + if (mode == REPLICA) { + out << QString(" QVariantList properties;"); + out << QString(" properties.reserve(%1);").arg(astClass.m_properties.size()); + foreach (const ASTProperty &property, astClass.m_properties) { + out << QString(" properties << QVariant::fromValue(" + property.type() + "(" + property.defaultValue() + "));"); + } + out << QString(" setProperties(properties);"); + } + + out << " }"; + out << "public:"; + + out << QString(" virtual ~%1() {}").arg(className); + + //First output properties + foreach (const ASTProperty &property, astClass.m_properties) { + if (property.modifier() == ASTProperty::Constant) + out << QString(" Q_PROPERTY(%1 %2 READ %2 CONSTANT)").arg(property.type(), property.name()); + else + out << QString(" Q_PROPERTY(%1 %2 READ %2 WRITE set%3 NOTIFY on%3Changed)").arg(property.type(), property.name(), cap(property.name())); + } + out << ""; + + //Next output getter/setter + if (mode == REPLICA) { + int i = 0; + foreach (const ASTProperty &property, astClass.m_properties) { + out << QString(" %1 %2() const").arg(property.type(), property.name()); + out << QString(" {"); + out << QString(" return propAsVariant(%1).value<%2>();").arg(i).arg(property.type()); + out << QString(" }"); + i++; + if (property.modifier() != ASTProperty::Constant) { + out << QString(""); + out << QString(" void set%3(%1 %2)").arg(property.type(), property.name(), cap(property.name())); + out << QString(" {"); + out << QString(" static int index = %1::staticMetaObject.indexOfProperty(\"%2\");").arg(className).arg( property.name()); + out << QString(" QVariantList args;"); + out << QString(" args << QVariant::fromValue(%1);").arg(property.name()); + out << QString(" send(QMetaObject::WriteProperty, index, args);"); + out << QString(" }"); + } + out << QString(""); + } + } + else + { + foreach (const ASTProperty &property, astClass.m_properties) { + out << QString(" %1 %2() const { return _%2;}").arg(property.type(), property.name()); + } + foreach (const ASTProperty &property, astClass.m_properties) { + if (property.modifier() != ASTProperty::Constant) { + out << QString(" void set%3(%1 %2)").arg(property.type(), property.name(), cap(property.name())); + out << QString(" {"); + out << QString(" if (%1 != _%1)").arg(property.name()); + out << QString(" {"); + out << QString(" _%1 = %1;").arg(property.name()); + out << QString(" Q_EMIT on%1Changed();").arg(cap(property.name())); + out << QString(" }"); + out << QString(" }"); + } + } + } + + //Next output property signals + out << ""; + out << "Q_SIGNALS:"; + foreach (const ASTProperty &property, astClass.m_properties) { + if (property.modifier() != ASTProperty::Constant) + out << QString(" void on%1Changed();").arg(cap(property.name())); + } + + foreach (const QString &signalName, astClass.m_signals) + out << QString(" void %1;").arg(signalName); + + if (!astClass.m_slots.isEmpty()) { + out << ""; + out << "public Q_SLOTS:"; + foreach (const QString &slot, astClass.m_slots) { + if (mode == SOURCE) { + out << QString(" virtual void %1 = 0;").arg(slot); + } else { + QRegExp re_slotArgs("\\s*(.*)\\s*\\(\\s*(.*)\\s*\\)"); + if (re_slotArgs.exactMatch(slot)) { + QStringList types, names; + const QStringList cap = re_slotArgs.capturedTexts(); + + const QString functionString = cap[1].trimmed(); + const QString argString = cap[2].trimmed(); + + if (!argString.isEmpty()) { + const QStringList argList = argString.split(','); + foreach (QString paramString, argList) { + paramString = paramString.trimmed(); + const QStringList tmp = paramString.split(QRegExp("\\s+")); + types << tmp[0]; + names << tmp[1]; + } + } + + out << QString(" void %1").arg(slot); + out << QString(" {"); + out << QString(" static int index = %1::staticMetaObject.indexOfSlot(\"%2(%3)\");").arg(className).arg(functionString).arg(types.join(", ")); + out << QString(" QVariantList args;"); + if (names.length() > 0) + out << QString(" args << %1;").arg(names.join(" << ")); + out << QString(" qDebug() << \"%1::%2\" << index;").arg(className).arg(functionString); + out << QString(" send(QMetaObject::InvokeMetaMethod, index, args);"); + out << QString(" }"); + } + } + } + } + + //Next output data members + out << ""; + out << "private:"; + if (mode == SOURCE) + { + foreach (const ASTProperty &property, astClass.m_properties) { + out << QString(" %1 _%2;").arg(property.type(), property.name()); + } + } + + out << "};"; + out << ""; +} diff --git a/tools/repc/RepCodeGenerator.h b/tools/repc/RepCodeGenerator.h new file mode 100644 index 0000000..a63ef93 --- /dev/null +++ b/tools/repc/RepCodeGenerator.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef REPCODEGENERATOR_H +#define REPCODEGENERATOR_H + +#include <QString> + +class AST; +class ASTClass; +struct POD; + +QT_BEGIN_NAMESPACE +class QStringList; +class QTextStream; +QT_END_NAMESPACE + +class RepCodeGenerator +{ +public: + enum Mode + { + REPLICA, + SOURCE + }; + + explicit RepCodeGenerator(const QString &fileName); + + bool generate(const AST &ast, Mode mode); + +private: + void generateHeader(Mode mode, QTextStream &out, const AST &ast); + QString generateMetaTypeRegistrationForPODs(const QVector<POD> &pods); + + void generatePOD(QTextStream &out, const POD &pod); + QString formatQPropertyDeclarations(const POD &pod); + QString formatConstructors(const POD &pod); + QString formatCopyConstructor(const POD &pod); + QString formatCopyAssignmentOperator(const POD &pod); + QString formatPropertyGettersAndSetters(const POD &pod); + QString formatSignals(const POD &pod); + QString formatDataMembers(const POD &pod); + QString formatMarshalingOperators(const POD &pod); + + void generateClass(Mode mode, QStringList &out, const ASTClass &astClasses, const QString &podMetaTypeRegistrationCode); + +private: + QString m_fileName; +}; + +#endif diff --git a/tools/repc/RepParser.cpp b/tools/repc/RepParser.cpp new file mode 100644 index 0000000..987c133 --- /dev/null +++ b/tools/repc/RepParser.cpp @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "RepParser.h" + +#include <QFile> +#include <QTextStream> + +ASTProperty::ASTProperty() + : m_modifier(ReadWrite) +{ +} + +ASTProperty::ASTProperty(const QString &type, const QString &name, const QString &defaultValue, Modifier modifier) + : m_type(type), m_name(name), m_defaultValue(defaultValue), m_modifier(modifier) +{ +} + +QString ASTProperty::type() const +{ + return m_type; +} + +QString ASTProperty::name() const +{ + return m_name; +} + +QString ASTProperty::defaultValue() const +{ + return m_defaultValue; +} + +ASTProperty::Modifier ASTProperty::modifier() const +{ + return m_modifier; +} + + +ASTClass::ASTClass(const QString &name) + : m_name(name) +{ +} + +bool ASTClass::isValid() const +{ + return !m_name.isEmpty(); +} + +QString ASTClass::name() const +{ + return m_name; +} + +//grammar +// R->WHITESPACE +// WHITESPACE: newlines, whitespaces +// R->POD +// R->CLASS DESCR +// DESCR->WHITESPACE IDENTIFIER CBRACKET +// DESCR->IDENTIFIER COBRACKET +// COBRACKET->WHITESPACE CLASSBODY +// COBRACKET->CLASSBODY +// CLASSBODY->WHITESPACE PROPERTY SEMICOLON +// CLASSBODY->PROPERTY SEMICOLON +// CLASSBODY->WHITESPACE SIGNAL SEMICOLON +// CLASSBODY->SIGNAL SEMICOLON +// CLASSBODY->WHITESPACE SLOT SEMICOLON +// CLASSBODY->SLOT SEMICOLON +// SEMICOLON->WHITESPACE CLASSBODY +// SEMICOLON->CLASSBODY +// SEMICOLON->WHITESPACE CCLOSEBRACKET +// SEMICOLON->CCLOSEBRACKET +// CCLOSEBRACKET->; WHITESPACE R +// CCLOSEBRACKET->; R +// SLOT->WHITESPACE SLOT +// SLOT->IDENTIFIER ROPENBRACKET +// ROPENBRACKET + +QRegExp re_class("class\\s*(\\S+)\\s*"); +QRegExp re_pod("POD\\s*(\\S+)\\s*\\(\\s*(.*)\\s*\\);?\\s*"); +QRegExp re_prop("\\s*PROP\\((.+)\\s+([^\\)=]+)(=(.*))?\\);?.*"); +QRegExp re_fixed("\\s*FIXED\\((.+)\\s+([^\\)=]+)(=(.*))?\\);?.*"); +QRegExp re_signal("\\s*SIGNAL\\(\\s*(.*)\\s*\\);?\\s*"); +QRegExp re_slot("\\s*SLOT\\(\\s*(.*)\\s*\\);?\\s*"); +QRegExp re_start("^\\{\\s*"); +QRegExp re_end("^\\};?\\s*"); + +RepParser::RepParser(const QString &fileName) + : m_fileName(fileName) +{ +} + +bool RepParser::parse() +{ + // clean up from previous run + m_ast.classes.clear(); + m_ast.pods.clear(); + m_ast.includes.clear(); + + QFile file(m_fileName); + if (!file.open(QIODevice::ReadOnly)) + return false; + + ASTClass astClass; + + QTextStream stream(&file); + while (!stream.atEnd()) { + const QString line = stream.readLine(); + + if (re_class.exactMatch(line)) { + // new Class declaration + astClass = ASTClass(re_class.capturedTexts()[1]); + } else if (re_pod.exactMatch(line)) { + POD pod; + pod.name = re_pod.capturedTexts()[1]; + + const QString argString = re_pod.capturedTexts()[2].trimmed(); + const QStringList argList = argString.split(","); + if (argList.length() == 0) + continue; + + foreach (const QString ¶mString, argList) { + const QStringList tmp = paramString.trimmed().split(QRegExp("\\s+")); + PODAttribute attr = { tmp[0], tmp[1] }; + pod.attributes.append(qMove(attr)); + } + + m_ast.pods.append(pod); + } else if (re_prop.exactMatch(line)) { + const QStringList params = re_prop.capturedTexts(); + + ASTProperty property(params[1], params[2], (params.length() == 5 ? params[4] : QString()), ASTProperty::ReadWrite); + astClass.m_properties << property; + } else if (re_fixed.exactMatch(line)) { + const QStringList params = re_prop.capturedTexts(); + + ASTProperty property(params[1], params[2], (params.length() == 5 ? params[4] : QString()), ASTProperty::Constant); + astClass.m_properties << property; + } else if (re_signal.exactMatch(line)) { + astClass.m_signals << re_signal.capturedTexts()[1]; + } else if (re_slot.exactMatch(line)) { + astClass.m_slots << re_slot.capturedTexts()[1]; + } else if (re_end.exactMatch(line)) { + m_ast.classes.append(astClass); + } else if (re_start.exactMatch(line)) { + } else { + m_ast.includes.append(line); + } + } + + return true; +} + +AST RepParser::ast() const +{ + return m_ast; +} diff --git a/tools/repc/RepParser.h b/tools/repc/RepParser.h new file mode 100644 index 0000000..cf95713 --- /dev/null +++ b/tools/repc/RepParser.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef REPPARSER_H +#define REPPARSER_H + +#include <QStringList> +#include <QVector> + +// A property of a Class declaration +class ASTProperty +{ +public: + enum Modifier + { + Constant, + Read, + ReadWrite + }; + + ASTProperty(); + ASTProperty(const QString &type, const QString &name, const QString &defaultValue, Modifier modifier); + + QString type() const; + QString name() const; + QString defaultValue() const; + Modifier modifier() const; + +private: + QString m_type; + QString m_name; + QString m_defaultValue; + Modifier m_modifier; +}; + +// A Class declaration +class ASTClass +{ +public: + explicit ASTClass(const QString& name = QString()); + bool isValid() const; + QString name() const; + +private: + friend class RepParser; + friend class RepCodeGenerator; + + QString m_name; + QVector<ASTProperty> m_properties; + QStringList m_signals; + QStringList m_slots; + +}; + +// The attribute of a POD +struct PODAttribute +{ + QString type; + QString name; +}; +Q_DECLARE_TYPEINFO(PODAttribute, Q_MOVABLE_TYPE); + +// A POD declaration +struct POD +{ + QString name; + QVector<PODAttribute> attributes; +}; + +// The AST representation of a .rep file +class AST +{ +public: + QVector<ASTClass> classes; + QVector<POD> pods; + QStringList includes; +}; + +class RepParser +{ +public: + explicit RepParser(const QString &fileName); + + bool parse(); + + AST ast() const; + +private: + QString m_fileName; + AST m_ast; +}; + +#endif diff --git a/tools/repc/main.cpp b/tools/repc/main.cpp new file mode 100644 index 0000000..9088103 --- /dev/null +++ b/tools/repc/main.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QCoreApplication> + +#include "RepCodeGenerator.h" +#include "RepParser.h" + +#include <cstdio> + +void usage() +{ + printf("repc [-s/-r] -i input-file -o output-file\n"); + printf(" -s outputs the source version from the template\n"); + printf(" -r outputs the replica version from the template\n"); + exit(-1); +} + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + if (app.arguments().size() < 3 || app.arguments().contains("--help")) + usage(); + + const int idxOfInput = app.arguments().indexOf("-i")+1; + const int idxOfOutput = app.arguments().indexOf("-o")+1; + if (idxOfInput <= 1 && idxOfOutput <= 1) + usage(); + + RepCodeGenerator::Mode mode; + if (app.arguments().contains("-s")) + mode = RepCodeGenerator::SOURCE; + else if (app.arguments().contains("-r")) + mode = RepCodeGenerator::REPLICA; + else + usage(); + + const QString input = app.arguments().at(idxOfInput); + const QString output = app.arguments().at(idxOfOutput); + + RepParser parser(input); + if (!parser.parse()) + return 1; + + RepCodeGenerator generator(output); + if (!generator.generate(parser.ast(), mode)) + return 2; + + return 0; +} diff --git a/tools/repc/repc.pro b/tools/repc/repc.pro new file mode 100644 index 0000000..f290bda --- /dev/null +++ b/tools/repc/repc.pro @@ -0,0 +1,6 @@ +option(host_build) + +load(qt_tool) + +SOURCES += main.cpp RepParser.cpp RepCodeGenerator.cpp +HEADERS += RepParser.h RepCodeGenerator.h diff --git a/tools/tools.pro b/tools/tools.pro new file mode 100644 index 0000000..509502d --- /dev/null +++ b/tools/tools.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += repc |