1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
|
/**************************************************************************
* *
* Regina - A Normal Surface Theory Calculator *
* Computational Engine *
* *
* Copyright (c) 1999-2025, Ben Burton *
* For further details contact Ben Burton (bab@debian.org). *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of the *
* License, or (at your option) any later version. *
* *
* As an exception, when this program is distributed through (i) the *
* App Store by Apple Inc.; (ii) the Mac App Store by Apple Inc.; or *
* (iii) Google Play by Google Inc., then that store may impose any *
* digital rights management, device limits and/or redistribution *
* restrictions that are required by its terms of service. *
* *
* This program 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 *
* General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
/*! \file packet/xmlpacketreader.h
* \brief Deals with parsing XML data for individual packets.
*/
#ifndef __REGINA_XMLPACKETREADER_H
#ifndef __DOXYGEN
#define __REGINA_XMLPACKETREADER_H
#endif
#include "regina-core.h"
#include "file/xml/xmlelementreader.h"
#include <memory>
namespace regina {
class Packet;
class XMLTreeResolver;
/**
* An XML element reader that reads the data for an individual packet.
*
* Generally a subclass of XMLPacketReader will be used to receive and
* store packets that you care about. However, if you simply wish to
* ignore a particular packet (and all of its descendants), you can use
* class XMLPacketReader itself for the packet(s) you wish to ignore.
*
* Routine packet() is used to return the packet that was read; see
* its documentation for further notes on how the packet should be
* constructed.
*
* Routines startSubElement() and endSubElement() should _not_ be
* overridden by derived classes. They determine whether the subelement
* is another packet element or a packet tag; if so then they deal with
* the subelement themselves (packet elements will be read using a new
* XMLPacketReader of the correct type), and if not then they call
* startContentSubElement() and endContentSubElement() which _should_
* be overridden for processing of non-packet XML subelements.
*
* If routine abort() is overridden, it _must_ at some point call
* XMLPacketReader::abort().
*
* The XML packet reader should read everything that
* Packet::writeXMLPacketData() writes, excluding the contents of
* Packet::writeXMLHeader(), Packet::writeXMLTreeData() and
* Packet::writeXMLFooter() (i.e., excluding the packet open/close XML tags,
* any of Regina's packet tags, and/or any child packets).
*
* The packet reader may assume that its parent packet (if one exists) has
* already been read from the file. The packet reader itself should not
* insert the new packet into the packet tree (the parent reader will do this).
*
* If the new packet needs to store pointers to other packets that might not
* have been read yet (such as a script packet that needs pointers to its
* variables), then it should queue a new XMLTreeResolutionTask to the
* XMLTreeResolver that was passed to its constructor. After the complete data
* file has been read, XMLTreeResolver::resolve() will run all of its queued
* tasks, at which point the new packet can resolve any dangling references.
*
* \nopython
*/
class XMLPacketReader : public XMLElementReader {
private:
std::shared_ptr<Packet> packet_;
/**< The packet that has been read and constructed. This is
\c null until commit() is called, at which point it is
non-null unless an error occurred during reading or
reconstruction. */
bool readingContent_ { false };
/**< Identifies whether we are currently reading an XML sub-element
that should contain data required to reconstruct this packet
(as opposed to generic packet content such as packet
tags or child packets). This is set to \c true before
calling startContentSubElement(), and is set to \c false
again after calling endContentSubElement(). */
protected:
XMLTreeResolver& resolver_;
/**< The master resolver that will be used to fix dangling packet
references after the entire XML file has been read. */
std::shared_ptr<Packet> parent_;
/**< The location in the packet tree beneath which this packet
should be inserted. This may only be \c null if (i) \a anon
is \c true, or (ii) this packet reader represents the root
\<regina\> or \<reginadata\> element. */
bool anon_;
/**< Identifies whether this packet appears within an
anonymous block. */
std::string label_;
/**< The label to assign to the packet being read. */
std::string id_;
/**< The string ID that uniquely identifies this packet in the
XML data file, or the empty string if this packet has no ID. */
public:
/**
* Creates a new packet element reader.
*
* \param resolver the master resolver that will be used to fix
* dangling packet references after the entire XML file has been read.
* \param parent the location in the packet tree beneath which this
* packet will be inserted, once it has been constructed. This _must_
* be non-null unless (i) \a anon is \c true, or (ii) this packet
* reader represents the root \<regina\> or \<reginadata\> element.
* \param anon \c true if this packet appears within an \<anon\> block.
* \param label the label that will be assigned to this packet,
* once it has been constructed. If this is the empty string,
* the packet label will not be set (which typically means the packet
* will have the default empty label).
* \param id the string ID that identifies this packet in the packet
* tree, as used for cross-referencing between packets in the XML
* data file, or the empty string if this packet has no ID.
*/
XMLPacketReader(XMLTreeResolver& resolver,
std::shared_ptr<Packet> parent, bool anon, std::string label,
std::string id);
/**
* Returns the packet that has been read and constructed by this
* element reader.
*
* This routine will be called at least once for every packet reader.
* The first time it is called will be after all packet-specific
* content should have been read (either immediately before processing
* packet tags and/or child packets, or during endElement() or abort()).
* See commit() for details on the precise timeline.
*
* This routine should therefore assume that it has received all
* the packet-specific data it will get, and should make its
* best attempt to construct a packet accordingly. It may
* return \c null if this is not possible, in which case the
* packet being read (and all its descendants) may be dropped from
* the packet tree.
*
* Once this routine gives a non-null return value, this function must
* continue to give the same return value from this point onwards
* (however, typically this function would not be called again).
*
* The new packet should not be given a packet label, and should not
* be inserted into the packet tree. These tasks will be managed by
* commit(). Likewise, the new packet should not be given any packet
* tags or child packets.
*
* The default implementation returns \c null.
*
* \return the packet that has been constructed, or \c null if
* this is not possible given the data that has been read.
*/
virtual std::shared_ptr<Packet> packetToCommit();
/**
* Used instead of startSubElement() for XML subelements that
* are not child packets or packet tags.
*
* The default implementation returns a new XMLElementReader
* which can be used to ignore the subelement completely.
*
* \param subTagName the name of the subelement opening tag.
* \param subTagProps the properties associated with the
* subelement opening tag.
* \return a newly created element reader that will be used to
* parse the subelement. This class should _not_ take care of
* the new reader's destruction; that will be done by the parser.
*/
virtual XMLElementReader* startContentSubElement(
const std::string& subTagName,
const regina::xml::XMLPropertyDict& subTagProps);
/**
* Used instead of endSubElement() for XML subelements that are
* not child packets or packet tags.
*
* The default implementation does nothing.
*
* \param subTagName the name of the subelement closing tag.
* \param subReader the child reader that was used to parse the
* subelement (this is the reader that was returned by the
* corresponding startContentSubElement() call). It is guaranteed
* that endElement() has already been called upon this child reader
* and that the child reader has not yet been destroyed.
*/
virtual void endContentSubElement(const std::string& subTagName,
XMLElementReader* subReader);
void endElement() override;
XMLElementReader* startSubElement(const std::string& subTagName,
const regina::xml::XMLPropertyDict& subTagProps) override;
void endSubElement(const std::string& subTagName,
XMLElementReader* subReader) override;
void abort(XMLElementReader *subReader) override;
protected:
/**
* Finishes off the packet under construction and inserts it
* into the packet tree.
*
* This routine will _always_ be called for each packet reader:
* either when the first tag/packet child is seen, or (if there are
* no tags or child packets) from endElement(), or (if necessary)
* from abort().
*
* At this point at which this routine is called, the full contents
* of the current packet should have been read from XML, and should
* be accessible via the routine packetToCommit(). This routine will:
*
* - fetch the packet from packetToCommit(), set its label,
* and register its ID with the resolver;
*
* - if \a anon_ is \c false, insert the packet in the tree beneath
* \a parent_;
*
* - if \a anon_ is \c true, add the packet to the resolver's
* anonymous pool.
*
* Therefore a side-effect of commit() is to ensure that the
* packet under construction has its ownership managed by some entity.
*
* It is safe to call this routine multiple times; once it
* receives and processes a non-null packet, subsequent calls
* will do nothing.
*/
void commit();
};
// Inline functions for XMLPacketReader
inline XMLPacketReader::XMLPacketReader(XMLTreeResolver& resolver,
std::shared_ptr<Packet> parent, bool anon, std::string label,
std::string id) :
resolver_(resolver), parent_(std::move(parent)), anon_(anon),
label_(std::move(label)), id_(std::move(id)) {
}
inline std::shared_ptr<Packet> XMLPacketReader::packetToCommit() {
return nullptr;
}
inline XMLElementReader* XMLPacketReader::startContentSubElement(
const std::string&, const regina::xml::XMLPropertyDict&) {
return new XMLElementReader();
}
inline void XMLPacketReader::endContentSubElement(const std::string&,
XMLElementReader*) {
}
} // namespace regina
#endif
|