From 766923a3da3935eb383d3d183a3ecceb744d4b03 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Thu, 9 Feb 2023 17:50:08 -0800 Subject: [PATCH] App: Add support for in-memory Metadata creation --- src/App/Metadata.cpp | 76 ++++++++++++++++++++++++--------------- src/App/Metadata.h | 6 ++++ src/App/MetadataPy.xml | 5 ++- src/App/MetadataPyImp.cpp | 61 ++++++++++++++++++++----------- 4 files changed, 98 insertions(+), 50 deletions(-) diff --git a/src/App/Metadata.cpp b/src/App/Metadata.cpp index 024747a2a5..cc70eecf10 100644 --- a/src/App/Metadata.cpp +++ b/src/App/Metadata.cpp @@ -30,7 +30,10 @@ #include "Metadata.h" +#include #include +#include +#include #include #include "App/Application.h" @@ -89,37 +92,13 @@ class XMLErrorHandler: public HandlerBase Metadata::Metadata(const fs::path &metadataFile) { - // Any exception thrown by the XML code propagates out and prevents object creation - XMLPlatformUtils::Initialize(); - - _parser = std::make_shared(); - _parser->setValidationScheme(XercesDOMParser::Val_Never); - _parser->setDoNamespaces(true); - - auto errHandler = std::make_unique(); - _parser->setErrorHandler(errHandler.get()); - #if defined(FC_OS_WIN32) - _parser->parse(reinterpret_cast(metadataFile.wstring().c_str())); + auto source = + LocalFileInputSource(reinterpret_cast(metadataFile.wstring().c_str())); #else - _parser->parse(metadataFile.string().c_str()); + auto source = LocalFileInputSource(XUTF8Str(metadataFile.string().c_str()).unicodeForm()); #endif - - auto doc = _parser->getDocument(); - _dom = doc->getDocumentElement(); - - auto rootTagName = StrXUTF8(_dom->getTagName()).str; - if (rootTagName != "package") { - throw Base::XMLBaseException( - "Malformed package.xml document: Root group not found"); - } - auto formatVersion = XMLString::parseInt(_dom->getAttribute(XUTF8Str("format").unicodeForm())); - switch (formatVersion) { - case 1: parseVersion1(_dom); break; - default: - throw Base::XMLBaseException( - "package.xml format version is not supported by this version of FreeCAD"); - } + loadFromInputSource(source); } Metadata::Metadata() : _dom(nullptr) {} @@ -137,6 +116,47 @@ Metadata::Metadata(const DOMNode *domNode, int format) : _dom(nullptr) } } +App::Metadata::Metadata(const std::string& rawData) + : _dom(nullptr) +{ + MemBufInputSource buffer( + reinterpret_cast(rawData.c_str()), rawData.size(), "raw data (in memory)"); + loadFromInputSource(buffer); +} + +void Metadata::loadFromInputSource(const InputSource& source) +{ + // Any exception thrown by the XML code propagates out and prevents object creation + XMLPlatformUtils::Initialize(); + + _parser = std::make_shared(); + _parser->setValidationScheme(XercesDOMParser::Val_Never); + _parser->setDoNamespaces(true); + + auto errHandler = std::make_unique(); + _parser->setErrorHandler(errHandler.get()); + + _parser->parse(source); + + auto doc = _parser->getDocument(); + _dom = doc->getDocumentElement(); + + auto rootTagName = StrXUTF8(_dom->getTagName()).str; + if (rootTagName != "package") { + throw Base::XMLBaseException( + "Malformed package.xml document: Root group not found"); + } + auto formatVersion = XMLString::parseInt(_dom->getAttribute(XUTF8Str("format").unicodeForm())); + switch (formatVersion) { + case 1: + parseVersion1(_dom); + break; + default: + throw Base::XMLBaseException( + "package.xml format version is not supported by this version of FreeCAD"); + } +} + Metadata::~Metadata() = default; std::string Metadata::name() const { return _name; } diff --git a/src/App/Metadata.h b/src/App/Metadata.h index 33696ec837..6d11bb5e02 100644 --- a/src/App/Metadata.h +++ b/src/App/Metadata.h @@ -206,6 +206,11 @@ public: */ Metadata(const XERCES_CPP_NAMESPACE::DOMNode *domNode, int format); + /** + * Treat the incoming rawData as metadata to be parsed. + */ + explicit Metadata(const std::string& rawData); + ~Metadata(); @@ -369,6 +374,7 @@ private: XERCES_CPP_NAMESPACE::DOMElement *_dom; std::shared_ptr _parser; + void loadFromInputSource(const XERCES_CPP_NAMESPACE::InputSource& source); void parseVersion1(const XERCES_CPP_NAMESPACE::DOMNode *startNode); void parseContentNodeVersion1(const XERCES_CPP_NAMESPACE::DOMElement *contentNode); diff --git a/src/App/MetadataPy.xml b/src/App/MetadataPy.xml index e4a35530b2..be05c18aea 100644 --- a/src/App/MetadataPy.xml +++ b/src/App/MetadataPy.xml @@ -28,7 +28,10 @@ Copy constructor. metadata : App.Metadata\n Metadata(file) Reads the XML file and provides access to the metadata it specifies. -file : str\n XML file name. +file : str\n XML file name.\n +Metadata(bytes) +Treats the bytes as UTF-8-encoded XML data and provides access to the metadata it specifies. +bytes : bytes\n Python bytes-like object. Metadata diff --git a/src/App/MetadataPyImp.cpp b/src/App/MetadataPyImp.cpp index 70f4cde745..d3708ada85 100644 --- a/src/App/MetadataPyImp.cpp +++ b/src/App/MetadataPyImp.cpp @@ -1,24 +1,24 @@ -/*************************************************************************** - * Copyright (c) 2021 Chris Hennes * - * * - * This file is part of the FreeCAD CAx development system. * - * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Library General Public * - * License as published by the Free Software Foundation; either * - * version 2 of the License, or (at your option) any later version. * - * * - * This library is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Library General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this library; see the file LICENSE.html. If not, * - * write to the Free Software Foundation, Inc., 59 Temple Place, * - * Suite 330, Boston, MA 02111-1307, USA * - * * - ***************************************************************************/ +/************************************************************************** +* * +* Copyright (c) 2022 FreeCAD Project Association * +* * +* This file is part of FreeCAD. * +* * +* FreeCAD is free software: you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as * +* published by the Free Software Foundation, either version 2.1 of the * +* License, or (at your option) any later version. * +* * +* FreeCAD is distributed in the hope that it will be useful, but * +* WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +* Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU Lesser General Public * +* License along with FreeCAD. If not, see * +* . * +* * +**************************************************************************/ #include "PreCompiled.h" @@ -63,6 +63,25 @@ int MetadataPy::PyInit(PyObject *args, PyObject * /*kwd*/) return 0; } + // Data may be passed directly in as a bytes-like object buffer + PyErr_Clear(); + Py_buffer dataBuffer; + if (PyArg_ParseTuple(args, "y*", &dataBuffer)) { + try { + // NB: This is making a copy of the buffer for simplicity, but that shouldn't be + // necessary. Use either a string_view or a span to avoid the copy in the future. + auto md = new Metadata( + std::string(static_cast(dataBuffer.buf), dataBuffer.len) + ); + setTwinPointer(md); + return 0; + } + catch (const Base::XMLBaseException&) { + // If the XML read failed, this might have been a path to a file, rather than a + // bytes-like object. Fall through to the next case. + } + } + // Main class constructor -- takes a file path, loads the metadata from it PyErr_Clear(); char *filename;