8#include <framework/gearbox/Gearbox.h>
9#include <framework/gearbox/GearDir.h>
10#include <framework/core/MRUCache.h>
11#include <framework/logging/Logger.h>
12#include <framework/utilities/Stream.h>
14#include <libxml/parser.h>
15#include <libxml/xinclude.h>
16#include <libxml/xmlIO.h>
18#include <boost/algorithm/string.hpp>
32 static int matchXmlUri(
const char* uri)
37 if (boost::starts_with(uri,
"file:/"))
return 0;
42 void* openXmlUri(
const char* uri)
44 B2DEBUG(200,
"Request to open " << uri);
46 return (
void*) context;
50 static int readXmlData(
void* context,
char* buffer,
int buffsize)
53 auto* gearContext =
static_cast<InputContext*
>(context);
54 return gearContext->readXmlData(buffer, buffsize);
58 static int closeXmlContext(
void* context)
60 B2DEBUG(200,
"Closing context");
61 auto* gearContext =
static_cast<InputContext*
>(context);
93 if (context)
return context;
95 B2ERROR(
"Could not find data for uri '" << uri <<
"'");
102 for (
const string& backend : backends) {
103 B2DEBUG(300,
"Adding InputHandler for '" << backend <<
"'");
106 string prefix(
"file");
107 string accessinfo(backend);
108 size_t colon = backend.find(
':');
109 if (colon != string::npos) {
110 prefix = backend.substr(0, colon);
111 accessinfo = backend.substr(colon + 1);
115 B2ERROR(
"Could not find input handler to handle '" << backend <<
"', ignoring");
122 B2ERROR(
"Problem creating input handler to handle '" << backend <<
"', ignoring");
139 B2FATAL(
"No backends defined, please use Gearbox::setBackends() first to specify how to access XML files.");
143 gearbox::readXmlData, gearbox::closeXmlContext);
151 xmlPopInputCallbacks();
153 if (!
m_xmlDocument) B2FATAL(
"Could not connect gearbox to " << name);
186 if (
m_xpathContext ==
nullptr) B2FATAL(
"Gearbox is not connected");
190 B2INFO(
"Override '" << poverride.
path <<
"' with '" << poverride.
value
191 <<
"' (unit: '" << poverride.
unit <<
"')");
192 xmlXPathObjectPtr result = xmlXPathEvalExpression((xmlChar*) query.c_str(),
m_xpathContext);
193 if (result !=
nullptr && result->type == XPATH_NODESET && !xmlXPathNodeSetIsEmpty(result->nodesetval)) {
195 int numNodes = xmlXPathNodeSetGetLength(result->nodesetval);
196 if (!poverride.
multiple && numNodes > 1) {
197 B2ERROR(
"Cannot override '" << poverride.
path <<
"': more than one node found");
200 B2DEBUG(200,
"Found " << numNodes <<
" nodes, overriding them all");
202 for (
int i = numNodes - 1; i >= 0; --i) {
203 xmlNodePtr node = result->nodesetval->nodeTab[i];
206 for (xmlNodePtr child = node->children; child; child = child->next) {
207 textOnly &= child->type == XML_TEXT_NODE;
208 if (!textOnly)
break;
211 B2ERROR(
"Cannot override '" << poverride.
path <<
"': not just text content");
214 xmlNodeSetContent(node, BAD_CAST poverride.
value.c_str());
217 if (node->type != XML_ELEMENT_NODE) {
218 if (!poverride.
unit.empty())
219 B2WARNING(
"Cannot set unit '" << poverride.
unit <<
"' on '"
220 << poverride.
path <<
"': not an element");
222 xmlSetProp(node, BAD_CAST
"unit", BAD_CAST poverride.
unit.c_str());
238 if (node->type != XML_NAMESPACE_DECL)
239 result->nodesetval->nodeTab[i] =
nullptr;
242 B2ERROR(
"Cannot override '" << poverride.
path <<
"': not found");
244 xmlXPathFreeObject(result);
250 if (
m_xpathContext ==
nullptr) B2FATAL(
"Gearbox is not connected");
257 B2DEBUG(1000,
"Gearbox XPath query: " << query);
258 xmlXPathObjectPtr result = xmlXPathEvalExpression((xmlChar*) query.c_str(),
m_xpathContext);
259 if (result !=
nullptr && result->type == XPATH_NODESET && !xmlXPathNodeSetIsEmpty(result->nodesetval)) {
260 value.numNodes = xmlXPathNodeSetGetLength(result->nodesetval);
261 xmlNodePtr node = result->nodesetval->nodeTab[0];
267 if (node->children && (
long)node->children->content > 0) {
268 xmlChar* valueString = xmlNodeListGetString(
m_xmlDocument, node->children, 1);
269 value.value = (
char*)valueString;
270 xmlFree(valueString);
273 xmlAttrPtr attribute = node->properties;
275 B2DEBUG(1001,
"Checking attribute " << attribute->name);
276 if (!strcmp((
char*)attribute->name,
"unit")) {
277 B2DEBUG(1001,
"found Unit " << attribute->children->content);
278 value.unit = (
char*)attribute->children->content;
281 attribute = attribute->next;
284 boost::trim(value.value);
285 boost::trim(value.unit);
289 B2DEBUG(1000,
"Gearbox XPath result: " << value.numNodes <<
", " << value.value <<
", " << value.unit);
291 xmlXPathFreeObject(result);
298 auto it = m_ownedObjects.find(path);
299 if (it != m_ownedObjects.end())
302 const string& value = getString(path);
306 throw gearbox::TObjectConversionError() << path;
308 m_ownedObjects[path] = object;
316 return GearDir(
"/Detector/DetectorComponent[@name='" + component +
"']/Content");
GearDir is the basic class used for accessing the parameter store.
Singleton class responsible for loading detector parameters from an XML file.
std::map< std::string, TObject * > m_ownedObjects
Map of queried objects (path -> TObject*).
std::map< std::string, gearbox::InputHandler::Factory * > m_registeredHandlers
Map of registered InputHandlers.
xmlDocPtr m_xmlDocument
Pointer to the libxml Document structure.
friend void * gearbox::openXmlUri(const char *)
friend to internal c-like function to interface libxml2 callback
MRUCache< std::string, PathValue > * m_parameterCache
Cache for already queried paths.
std::vector< PathOverride > m_overrides
the existing overrides
std::vector< gearbox::InputHandler * > m_handlers
List of input handlers which will be used to find resources.
xmlXPathContextPtr m_xpathContext
Pointer to the libxml XPath context.
Class implementing a generic Most Recently Used cache.
Class representing a resource context for gearbox.
std::string ensureNode(const std::string &path) const
make sure the path really corresponds to an XPath node expression by removing trailing slashes
static Gearbox & getInstance()
Return reference to the Gearbox instance.
virtual const TObject * getTObject(const std::string &path) const noexcept(false) override
Get the parameter path as a TObject.
~Gearbox()
Free structures on destruction.
Gearbox()
Singleton: private constructor.
void overridePathValue(const PathOverride &poverride)
Change the value of a given path expression.
PathValue getPathValue(const std::string &path) const
Return the (cached) value of a given path.
void clearBackends()
Clear list of backends.
void close()
Free internal structures of previously parsed tree and clear cache.
void setBackends(const std::vector< std::string > &backends)
Select the backends to use to find resources.
gearbox::InputContext * openXmlUri(const std::string &uri) const
Function to be called when libxml requests a new input uri to be opened.
void open(const std::string &name="Belle2.xml", size_t cacheSize=c_DefaultCacheSize)
Open connection to backend and parse tree.
GearDir getDetectorComponent(const std::string &component)
Return GearDir representing a given DetectorComponent.
TObject * deserializeEncodedRawData(const std::string &base64Data)
Convert given serialized raw data back into TObject.
Abstract base class for different kinds of events.
Struct to override a path in the XML file with a custom value.
bool multiple
if true, override all nodes when more than one node matches the XPath expression, bail otherwise
std::string path
XPath expression of the path to override.
std::string value
New value.
Struct for caching results from the xml file.