LIMA
Libre Multilingual Analyzer — C++ API
Loading...
Searching...
No Matches
bowXMLReader.cpp
Go to the documentation of this file.
1// Copyright 2002-2022 CEA LIST
2// SPDX-FileCopyrightText: 2022 CEA LIST <gael.de-chalendar@cea.fr>
3//
4// SPDX-License-Identifier: MIT
5
6/************************************************************************
7 * @author Besancon Romaric (besanconr@zoe.cea.fr)
8 * @date Wed May 5 2004
9 ***********************************************************************/
10
11#include "bowXMLReader.h"
12#include "bowTerm.h"
13#include "bowNamedEntity.h"
14#include "bowDocumentST.h"
19
20#include <QDate>
21#include <QFile>
22#include <QXmlStreamReader>
23
24using namespace Lima::Common::Misc;
25
26namespace Lima {
27namespace Common {
28namespace BagOfWords {
29
30//***********************************************************************
31// local exception
32//***********************************************************************
33class NoAttributeException : public std::runtime_error
34{
35public:
36 NoAttributeException(const std::string& mess):runtime_error(mess) {}
37};
38
40{
41 friend class BoWXmlReader;
42
43 public:
44 BoWXmlReaderPrivate(const std::string& filename,
45 std::ostream& output);
47
48 bool parse(QIODevice *device);
49 QString errorString() const;
50
51 void readBowDocument();
52 void readBowText();
53 void readHierarchy();
54 void readProperty();
55 void readProperties();
56 void readTokens();
57 void readBowToken();
58 void readBowTerm();
59 void readBowNamedEntity();
60 void readParts();
61 void readBowTokenRef();
62 void readFeature();
63
65 {
66 public:
68 CurrentComplexToken(boost::shared_ptr< BoWComplexToken > tok):
69 token(tok),currentPart(0),head(0) {}
70 boost::shared_ptr< BoWComplexToken > token;
71 uint64_t currentPart;
72 uint64_t head;
73 };
74
77 uint64_t position;
78 uint64_t length;
79 uint64_t id;
80
81 QXmlStreamReader m_reader;
82 std::ostream& m_outputStream;
84 boost::shared_ptr< BoWText > m_currentBoWText;
86 std::vector<CurrentComplexToken> m_currentComplexToken;
87 std::map<uint64_t, boost::shared_ptr< BoWToken > > m_refMap;
88
89 // private functions
90 uint64_t getIntAttribute(const QXmlStreamReader& attributes, const char* name) const;
91 float getFloatAttribute(const QXmlStreamReader& attributes, const char* name) const;
92 std::string getStringAttribute(const QXmlStreamReader& attributes, const char* name) const;
93 QString getQStringAttribute(const QXmlStreamReader& attributes, const char* name) const;
94 QDate getDateAttribute(const QXmlStreamReader& attributes, const char* name) const;
95
96 bool addProperty(const QXmlStreamReader& attributes);
97
98 uint64_t
99 getTokenAttributes(const QXmlStreamReader& attributes,
102 uint64_t& position,
103 uint64_t& length,
104 uint64_t& id) const;
105};
106
107
108BoWXmlReader::BoWXmlReader(const std::string& filename,
109 std::ostream& output):
110m_d(new BoWXmlReaderPrivate(filename, output))
111{
112}
113
115{
116 delete m_d;
117}
118
119//**********************************************************************
120// xerces handler functions
121//**********************************************************************
123 std::ostream& output):
124 m_reader(),
125 m_outputStream(output),
126 m_currentBoWDocument(),
127 m_currentBoWText(),
128 m_currentComplexToken(),
129 m_refMap()
130{
131 QFile file(filename.c_str());
132 if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
135 QString(QLatin1String("Error opening %1")).arg(filename.c_str()).toUtf8().constData(),
137 if (!parse(&file))
138 {
140 throw XMLException(QString(QLatin1String("Error parsing %1: %2"))
141 .arg(filename.c_str())
142 .arg(errorString()).toUtf8().constData());
143 }
144}
145
146bool BoWXmlReaderPrivate::parse(QIODevice *device)
147{
149 LDEBUG << "BoWXmlReaderPrivate::parse";
150 m_reader.setDevice(device);
151 if (m_reader.readNextStartElement()) {
152 if (m_reader.name() == QLatin1String("bowDocument"))
153 {
155 }
156 else if (m_reader.name() == QLatin1String("bowText"))
157 {
158 readBowText();
159 }
160 else
161 {
162 m_reader.raiseError(QObject::tr("The file is not a LIMA BoW XML file."));
163 }
164 }
165 return !m_reader.error();
166}
167
169{
171 LTRACE << "BoWXmlReaderPrivate::readBowDocument" << m_reader.name();
172 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("bowDocument"));
173
175
176 while (m_reader.readNextStartElement()) {
177 if (m_reader.name() == QLatin1String("bowText"))
178 readBowText();
179 else if (m_reader.name() == QLatin1String("hierarchy"))
181 else
182 m_reader.raiseError(QObject::tr("Expected a bowText or a hierarchy but got a %1.").arg(m_reader.name()));
183 }
184 m_currentBoWText=boost::shared_ptr <Lima::Common::BagOfWords::BoWText >();
185}
186
188{
190 LTRACE << "BoWXmlReaderPrivate::readBowText" << m_reader.name();
191 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("bowText"));
192
193 m_currentBoWText= boost::shared_ptr< BoWText >(new BoWText());
194 while (m_reader.readNextStartElement()) {
195 if (m_reader.name() == QLatin1String("tokens"))
196 readTokens();
197 else
198 m_reader.raiseError(QObject::tr("Expected a tokens but got a %1.").arg(m_reader.name()));
199 }
200}
201
203{
205 LTRACE << "BoWXmlReaderPrivate::readHierarchy" << m_reader.name();
206 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("hierarchy"));
207
208 m_currentBoWText = boost::shared_ptr< BoWText >(new BoWText());
209 bool isIndexingNode(false);
210 try {
211 auto indexingNode = getStringAttribute(m_reader, "indexingNode");
212 if (indexingNode == "yes")
213 {
214 isIndexingNode = true;
215 }
216 }
217 catch (NoAttributeException& )
218 {
219 // do nothing -> is not indexing node
220 }
221 if (isIndexingNode)
222 {
224 m_currentBoWText = boost::shared_ptr< BoWText >(new BoWText());
225 }
226 else
227 {
229 }
230 std::string elementName("");
231 try
232 {
233 elementName = getStringAttribute(m_reader, "elementName");
234 }
235 catch (NoAttributeException& )
236 {
237 LERROR << "missing attribute elementName in " << m_reader.name();
238 }
240 while (m_reader.readNextStartElement())
241 {
242 if (m_reader.name() == QLatin1String("bowText"))
243 readBowText();
244 else if (m_reader.name() == QLatin1String("hierarchy"))
246 else
247 m_reader.raiseError(QObject::tr("Expected a bowText or a hierarchy but got a %1.").arg(m_reader.name()));
248 }
249
251 if (m_currentBoWText !=0)
252 {
253 BoWBinaryWriter writer;
255 m_currentBoWText = boost::shared_ptr< BoWText >();
256 }
257}
258
260{
262 LTRACE << "BoWXmlReaderPrivate::readProperty" << m_reader.name();
263 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("property"));
264
266}
267
269{
271 LTRACE << "BoWXmlReaderPrivate::readProperties" << m_reader.name();
272 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("properties"));
273
275 while (m_reader.readNextStartElement()) {
276 if (m_reader.name() == QLatin1String("property"))
277 readProperty();
278 else
279 m_reader.raiseError(QObject::tr("Expected a property but got a %1.").arg(m_reader.name()));
280 }
283}
284
286{
288 LTRACE << "BoWXmlReaderPrivate::readTokens" << m_reader.name();
289 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("tokens"));
290
291 if (m_currentBoWText != nullptr)
292 {
293 m_currentBoWText->clear();
294 }
295 else
296 {
297 LERROR << "<tokens> found whereas no BoWText initialized";
298 }
299 while (m_reader.readNextStartElement()) {
300 if (m_reader.name() == QLatin1String("bowToken"))
301 readBowToken();
302 else if (m_reader.name() == QLatin1String("bowTerm"))
303 readBowTerm();
304 else if (m_reader.name() == QLatin1String("bowNamedEntity"))
306 else
307 m_reader.raiseError(QObject::tr("Expected a bowToken, bowTerm or a bowNamedEntity but got a %1.")
308 .arg(m_reader.name()));
309 }
311 //@todo
312 m_currentBoWText->writeBoWText(m_outputStream);
313}
314
316{
318 LTRACE << "BoWXmlReaderPrivate::readBowToken" << m_reader.name();
319 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("bowToken"));
320
322 LDEBUG << lemma << category << position << length << id ;
323 auto token = boost::shared_ptr< BoWToken >(new BoWToken(lemma,category,position,length));
324 m_refMap[id] = token;
325 if (m_currentComplexToken.empty())
326 {
327 m_currentBoWText->push_back(token);
328 }
329 else
330 {
331 if (m_currentComplexToken.back().currentPart ==
332 m_currentComplexToken.back().head) {
333 m_currentComplexToken.back().token->addPart(token,true);
334 }
335 else
336 {
337 m_currentComplexToken.back().token->addPart(token);
338 }
339 m_currentComplexToken.back().currentPart++;
340 // token has been cloned in complex token
341 }
342 m_reader.skipCurrentElement();
343}
344
346{
348 LTRACE << "BoWXmlReaderPrivate::readBowTerm" << m_reader.name();
349 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("bowTerm"));
350
352 // use empty lemma: no need to store lemma for compound
353 auto term = boost::shared_ptr< BoWTerm >(new BoWTerm(QString(), category, position, length));
354 m_refMap[id] = term;
356 while (m_reader.readNextStartElement())
357 {
358 if (m_reader.name() == QLatin1String("parts"))
359 readParts();
360 else
361 m_reader.raiseError(QObject::tr("Expected a parts but got a %1.").arg(m_reader.name()));
362 }
363 auto token = m_currentComplexToken.back().token;
364 m_currentComplexToken.pop_back();
365 if (m_currentComplexToken.empty())
366 {
367 m_currentBoWText->push_back(token);
368 }
369 else
370 { // this complex token is a part of another
371 if (m_currentComplexToken.back().currentPart == m_currentComplexToken.back().head)
372 {
373 m_currentComplexToken.back().token->addPart(token,true);
374 }
375 else
376 {
377 m_currentComplexToken.back().token->addPart(token);
378 }
379 m_currentComplexToken.back().currentPart++;
380 // token has been cloned in addPart => delete it
381 }
382}
383
385{
387 LTRACE << "BoWXmlReaderPrivate::readBowNamedEntity" << m_reader.name();
388 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("bowNamedEntity"));
389
391 auto typeName = getQStringAttribute(m_reader, "type");
393 try
394 {
395 entityType = MediaticData::MediaticData::single().getEntityType(typeName);
396 }
397 catch (const LimaException& e)
398 {
399 // use empty lemma: no need to store lemma for compound
400 QString errorString;
401 QTextStream qts(&errorString);
402 qts << __FILE__ << ", line" << __LINE__
403 << "Unknown entity type" << typeName << this->errorString();
406 }
407 auto ne = boost::shared_ptr< BoWNamedEntity >(
408 new BoWNamedEntity(QString(),
409 category, entityType,
410 position, length));
411 m_refMap[id] = ne;
413
414 while (m_reader.readNextStartElement()) {
415 if (m_reader.name() == QLatin1String("parts"))
416 readParts();
417 else if (m_reader.name() == QLatin1String("feature"))
418 readFeature();
419 else
420 m_reader.raiseError(QObject::tr("Expected a parts or feature but got a %1.").arg(m_reader.name()));
421 }
422 auto token = m_currentComplexToken.back().token;
423 m_currentComplexToken.pop_back();
424 if (m_currentComplexToken.empty())
425 {
426 m_currentBoWText->push_back(token);
427 }
428 else
429 { // this complex token is a part of another
430 if (m_currentComplexToken.back().currentPart == m_currentComplexToken.back().head)
431 {
432 m_currentComplexToken.back().token->addPart(token,true);
433 }
434 else
435 {
436 m_currentComplexToken.back().token->addPart(token);
437 }
438 m_currentComplexToken.back().currentPart++;
439 // token has been cloned in addPart => delete it
440 }
441 m_reader.skipCurrentElement();
442}
443
445{
447 LTRACE << "BoWXmlReaderPrivate::readParts" << m_reader.name();
448 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("parts"));
449
450 m_currentComplexToken.back().head = getIntAttribute(m_reader,"head");
451 m_currentComplexToken.back().currentPart = 0;
452
453 while (m_reader.readNextStartElement()) {
454 if (m_reader.name() == QLatin1String("bowToken"))
455 readBowToken();
456 else if (m_reader.name() == QLatin1String("bowTerm"))
457 readBowTerm();
458 else if (m_reader.name() == QLatin1String("bowNamedEntity"))
460 else
461 m_reader.raiseError(QObject::tr("Expected a bowToken, bowTerm or a bowNamedEntity but got a %1.")
462 .arg(m_reader.name()));
463 }
464}
465
467{
469 LTRACE << "BoWXmlReaderPrivate::readBowTokenRef" << m_reader.name();
470 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("bowTokenRef"));
471
472 uint64_t refId = getIntAttribute(m_reader,"refId");
473 m_currentComplexToken.back().token->addPart(m_refMap[refId]);
474 m_reader.skipCurrentElement();
475}
476
478{
480 LTRACE << "BoWXmlReaderPrivate::readFeature" << m_reader.name();
481 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("feature"));
482
483 auto name = getStringAttribute(m_reader,"name");
484 auto value = getQStringAttribute(m_reader,"value");
485 boost::dynamic_pointer_cast<BoWNamedEntity>(m_currentComplexToken.back().token)->
486 setFeature(name,value);
487 m_reader.skipCurrentElement();
488}
489
490// -----------------------------------------------------------------------
491// helper functions for parsing
492// -----------------------------------------------------------------------
493bool BoWXmlReaderPrivate::addProperty(const QXmlStreamReader& attributes)
494{
495 auto propName=getStringAttribute(attributes,"name");
496 auto typName=getStringAttribute(attributes,"type");
497 try
498 {
499 if (typName == "int")
500 {
501 m_currentProperties.setIntValue(propName, getIntAttribute(attributes, "value"));
502 }
503 else if (typName == "string")
504 {
505 m_currentProperties.setStringValue(propName, getStringAttribute(attributes, "value"));
506 }
507 else if (typName == "date")
508 {
509 m_currentProperties.setDateValue(propName, getDateAttribute(attributes, "value"));
510 }
511 else if (typName == "dateRange")
512 {
513 auto dateBegin = getDateAttribute(attributes, "valueBegin");
514 auto dateEnd = getDateAttribute(attributes, "valueEnd");
515 m_currentProperties.setDateIntervalValue(propName, std::make_pair(dateBegin, dateEnd));
516 }
517 else if (typName == "multString")
518 {
519 m_currentProperties.addStringValue(propName, getStringAttribute(attributes, "value"));
520 }
521 else if (typName == "multWeightedId")
522 {
523 auto val = getStringAttribute(attributes, "value");
524 auto score = getFloatAttribute(attributes, "weight");
525 m_currentProperties.addWeightedPropValue(propName, std::make_pair(val, score));
526 }
527 }
528 catch (std::exception& e)
529 {
531 LERROR << "Error reading doc infos: " << e.what();
532 return false;
533 }
534 return true;
535}
536
537uint64_t BoWXmlReaderPrivate::getTokenAttributes(const QXmlStreamReader& attributes,
538 LimaString& lemma,
539 LinguisticCode& category,
540 uint64_t& position,
541 uint64_t& length,
542 uint64_t& id) const
543{
544 lemma = getQStringAttribute(attributes,"lemma");
545 category = LinguisticCode::fromUInt(getIntAttribute(attributes,"category"));
546 position = getIntAttribute(attributes,"position");
547 length = getIntAttribute(attributes,"length");
548 id = getIntAttribute(attributes,"id");
549 return 0;
550}
551
552uint64_t BoWXmlReaderPrivate::getIntAttribute(const QXmlStreamReader& attributes, const char* name) const
553{
554 // do not make conversion at once in case the getValue fails
555 bool ok;
556 auto attr = attributes.attributes().value(name).toULongLong(&ok);
557
558 if (!ok)
559 {
560 std::ostringstream oss;
561 oss << "Cannot convert attribute \"" << name << "\"=\""
562 << attributes.attributes().value(name).toString().toStdString()
563 << "\" to uint64_t. " << errorString().toStdString();
564 throw NoAttributeException(oss.str());
565 }
566
567 return attr;
568}
569
570float BoWXmlReaderPrivate::getFloatAttribute(const QXmlStreamReader& attributes, const char* name) const
571{
572 // do not make conversion at once in case the getValue fails
573 bool ok;
574 auto attr = attributes.attributes().value(name).toFloat(&ok);
575
576 if (!ok)
577 {
578 std::ostringstream oss;
579 oss << "Cannot convert attribute \"" << name << "\"=\""
580 << attributes.attributes().value(name).toString().toStdString()
581 << "\" to float. " << errorString().toStdString();
582 throw NoAttributeException(oss.str());
583 }
584
585 return attr;
586}
587
588std::string BoWXmlReaderPrivate::getStringAttribute(const QXmlStreamReader& attributes, const char* name) const
589{
590 return getQStringAttribute(attributes,name).toStdString();
591}
592
593QDate BoWXmlReaderPrivate::getDateAttribute(const QXmlStreamReader& attributes, const char* name) const
594{
596 auto strDate = attributes.attributes().value(name).toString();
597
598 if (strDate == "NotADate")
599 {
600 LERROR << "BoWXMLHandler::getDateAttribute read strDate \""<<strDate<<"\"";
601 return QDate();
602 }
603
604 LDEBUG << "BoWXMLHandler::getDateAttribute read strDate \""<<strDate<<"\" with format \"yyyyMMdd\"";
605 auto date = QDate::fromString(strDate, "yyyyMMdd");
606 if (!date.isValid())
607 {
609 LERROR << "BoWXMLHandler::getDateAttribute Error trying to read date: " << strDate;
610 return QDate();
611 }
612 return date;
613}
614
615QString BoWXmlReaderPrivate::getQStringAttribute(const QXmlStreamReader& attributes, const char* name) const
616{
617 return attributes.attributes().value(name).toString();
618}
619
621{
623 auto errorStr = QObject::tr("%1, Line %2, column %3")
624 .arg(m_reader.errorString())
625 .arg(m_reader.lineNumber())
626 .arg(m_reader.columnNumber());
627 LERROR << errorStr;
628 return errorStr;
629}
630
631} // end namespace
632} // end namespace
633} // end namespace
#define LIMA_EXCEPTION_SELECT_LOGINIT(X, Y, Z)
This macro writes the message Y to the error stream configured by its first parameter X,...
Definition LimaCommon.h:332
#define LTRACE
Definition LimaCommon.h:156
#define LDEBUG
Definition LimaCommon.h:157
#define LERROR
Definition LimaCommon.h:161
#define PROPERTYCODELOGINIT
Definition LimaCommon.h:213
#define BOWLOGINIT
Definition LimaCommon.h:201
void writeBoWText(std::ostream &file, const BoWText &bowText) const
represent a document as a Bag Of Word, with associated document properties.
Definition bowDocument.h:57
This is a complex token used to represent a named entity token.
This is a complex token used to represent a multiword term.
Definition bowTerm.h:36
This class represents a list of elements, that are pointers on polymmorphic tokens that can be simple...
Definition bowText.h:48
This class contains the representation of an element of the bag of words.
Definition bowToken.h:46
CurrentComplexToken(boost::shared_ptr< BoWComplexToken > tok)
Common::Misc::GenericDocumentProperties m_currentProperties
QString getQStringAttribute(const QXmlStreamReader &attributes, const char *name) const
std::string getStringAttribute(const QXmlStreamReader &attributes, const char *name) const
std::vector< CurrentComplexToken > m_currentComplexToken
uint64_t getTokenAttributes(const QXmlStreamReader &attributes, LimaString &lemma, LinguisticCode &category, uint64_t &position, uint64_t &length, uint64_t &id) const
boost::shared_ptr< BoWText > m_currentBoWText
uint64_t getIntAttribute(const QXmlStreamReader &attributes, const char *name) const
float getFloatAttribute(const QXmlStreamReader &attributes, const char *name) const
QDate getDateAttribute(const QXmlStreamReader &attributes, const char *name) const
std::map< uint64_t, boost::shared_ptr< BoWToken > > m_refMap
bool addProperty(const QXmlStreamReader &attributes)
BoWXmlReaderPrivate(const std::string &filename, std::ostream &output)
XML input/output for BoW elements.
BoWXmlReader(const std::string &filename, std::ostream &output)
EntityType getEntityType(const LimaString &entityName) const
entity types manager
void addWeightedPropValue(const std::string &propName, const std::pair< std::string, float > &str)
void setStringValue(const std::string &propName, const std::string &val)
void addStringValue(const std::string &propName, const std::string &str)
void setDateIntervalValue(const std::string &propName, const std::pair< QDate, QDate > &val)
void setDateValue(const std::string &propName, const QDate &val)
void setIntValue(const std::string &propName, const uint64_t &val)
The main LIMA exception class.
Definition LimaCommon.h:262
static LinguisticCode fromUInt(uint64_t v)
Definition StdBitset.h:65
static const MediaticData & single()
const singleton accessor
Definition Singleton.h:51
Throw this exception when encountering problems with XML files opening or parsing.
Definition LimaCommon.h:470
void writeOneByteInt(std::ostream &file, const uint8_t number)
write a integer coded on one byte format in a file
void writeStringField(ostream &file, const string &s)
NAUTITIA.
QString LimaString
Definition LimaString.h:33