8#include "capalertinfo.h"
9#include "capalertmessage.h"
11#include "capnamedvalue.h"
12#include "capreference.h"
14#include <KLocalizedString>
18#include <QStringTokenizer>
33template<
typename QStringT,
typename EnumT, std::
size_t N>
34static std::optional<EnumT> stringToValue(
const QStringT &s,
const MapEntry<EnumT> (&map)[N])
36 const auto it = std::lower_bound(std::begin(map), std::end(map), s, [](
auto lhs,
auto rhs) {
37 return QLatin1String(lhs.name) < rhs;
39 if (it != std::end(map) && QLatin1String((*it).name) == s) {
46static constexpr const MapEntry<CAPAlertInfo::Category> category_map[] = {
47 {
"CBRNE", CAPAlertInfo::Category::CBRNE},
48 {
"Env", CAPAlertInfo::Category::Environmental},
49 {
"Fire", CAPAlertInfo::Category::Fire},
50 {
"Geo", CAPAlertInfo::Category::Geophysical},
51 {
"Health", CAPAlertInfo::Category::Health},
52 {
"Infra", CAPAlertInfo::Category::Infrastructure},
53 {
"Met", CAPAlertInfo::Category::Meteorological},
54 {
"Other", CAPAlertInfo::Category::Other},
55 {
"Rescue", CAPAlertInfo::Category::Rescue},
56 {
"Safety", CAPAlertInfo::Category::Safety},
57 {
"Security", CAPAlertInfo::Category::Security},
58 {
"Transport", CAPAlertInfo::Category::Transport},
61enum class Tags { ALERT, IDENTIFIER, SENDER, SENT_TIME, STATUS, MSG_TYPE, SCOPE, NOTE, INFO, REFERENCES };
63static constexpr const MapEntry<Tags> tag_map[] = {
64 {
"alert", Tags::ALERT},
65 {
"identifier", Tags::IDENTIFIER},
67 {
"msgType", Tags::MSG_TYPE},
69 {
"references", Tags::REFERENCES},
70 {
"scope", Tags::SCOPE},
71 {
"sender", Tags::SENDER},
72 {
"sent", Tags::SENT_TIME},
73 {
"status", Tags::STATUS},
98static constexpr const MapEntry<InfoTags> info_tag_map[] = {
99 {
"area", InfoTags::AREA},
100 {
"category", InfoTags::CATEGORY},
101 {
"certainty", InfoTags::CERTAINITY},
102 {
"contact", InfoTags::CONTACT},
103 {
"description", InfoTags::DESCRIPTION},
104 {
"effective", InfoTags::EFFECTIVE_TIME},
105 {
"event", InfoTags::EVENT},
106 {
"eventCode", InfoTags::EVENTCODE},
107 {
"expires", InfoTags::EXPIRE_TIME},
108 {
"headline", InfoTags::HEADLINE},
109 {
"instruction", InfoTags::INSTRUCTION},
110 {
"language", InfoTags::LANGUAGE},
111 {
"onset", InfoTags::ONSET_TIME},
112 {
"parameter", InfoTags::PARAMETER},
113 {
"responseType", InfoTags::RESPONSETYPE},
114 {
"senderName", InfoTags::SENDERNAME},
115 {
"severity", InfoTags::SEVERITY},
116 {
"urgency", InfoTags::URGENCY},
117 {
"web", InfoTags::WEB},
120static constexpr const MapEntry<CAPAlertMessage::Status> status_map[] = {
124 {
"System", CAPAlertMessage::Status::System},
128static constexpr const MapEntry<CAPAlertMessage::MessageType> msgtype_map[] = {
136static constexpr const MapEntry<CAPAlertMessage::Scope> scope_map[] = {
142static constexpr const MapEntry<CAPAlertInfo::ResponseType> response_type_map[] = {
143 {
"AllClear", CAPAlertInfo::ResponseType::AllClear},
144 {
"Assess", CAPAlertInfo::ResponseType::Assess},
145 {
"Avoid", CAPAlertInfo::ResponseType::Avoid},
146 {
"Evacuate", CAPAlertInfo::ResponseType::Evacuate},
147 {
"Execute", CAPAlertInfo::ResponseType::Execute},
148 {
"Monitor", CAPAlertInfo::ResponseType::Monitor},
149 {
"None", CAPAlertInfo::ResponseType::None},
150 {
"Prepare", CAPAlertInfo::ResponseType::Prepare},
151 {
"Shelter", CAPAlertInfo::ResponseType::Shelter},
154static constexpr const MapEntry<CAPAlertInfo::Urgency> urgency_map[] = {
155 {
"Expected", CAPAlertInfo::Urgency::Expected},
156 {
"Future", CAPAlertInfo::Urgency::Future},
157 {
"Immediate", CAPAlertInfo::Urgency::Immediate},
158 {
"Past", CAPAlertInfo::Urgency::Past},
159 {
"Unknown", CAPAlertInfo::Urgency::UnknownUrgency},
162static constexpr const MapEntry<CAPAlertInfo::Severity> severity_map[] = {
163 {
"Extreme", CAPAlertInfo::Severity::Extreme},
164 {
"Minor", CAPAlertInfo::Severity::Minor},
165 {
"Moderate", CAPAlertInfo::Severity::Moderate},
166 {
"Severe", CAPAlertInfo::Severity::Severe},
167 {
"Unknown", CAPAlertInfo::Severity::UnknownSeverity},
170static constexpr const MapEntry<CAPAlertInfo::Certainty> certainty_map[] = {
171 {
"Likely", CAPAlertInfo::Certainty::Likely},
172 {
"Observed", CAPAlertInfo::Certainty::Observed},
173 {
"Possible", CAPAlertInfo::Certainty::Possible},
174 {
"Unknown", CAPAlertInfo::Certainty::UnknownCertainty},
175 {
"Unlikely", CAPAlertInfo::Certainty::Unlikely},
178[[nodiscard]]
static CAPPolygon stringToPolygon(QStringView str)
183 const auto idx = coordinate.indexOf(
','_L1);
187 bool latOk =
false, lonOk =
false;
188 res.push_back({coordinate.left(idx).toFloat(&latOk), coordinate.mid(idx + 1).toFloat(&lonOk)});
189 if (!latOk || !lonOk) {
196CAPParser::CAPParser(
const QByteArray &data)
201 while (m_xml.readNextStartElement()) {
202 if (m_xml.name() == QStringLiteral(
"alert")) {
208 qWarning() <<
"Not a CAP XML";
213CAPAlertMessage CAPParser::parse()
215 CAPAlertMessage entry;
216 while (m_xml.readNextStartElement()) {
217 const auto tag = stringToValue(m_xml.name(), tag_map);
219 m_xml.skipCurrentElement();
223 case Tags::IDENTIFIER:
224 entry.setIdentifier(m_xml.readElementText());
227 entry.setSender(m_xml.readElementText());
229 case Tags::SENT_TIME:
233 const auto elementText = m_xml.readElementText();
234 const auto status = stringToValue(elementText, status_map);
238 qWarning() <<
"Unknown status field" << elementText;
242 case Tags::MSG_TYPE: {
243 const auto elementText = m_xml.readElementText();
244 const auto msgType = stringToValue(elementText, msgtype_map);
246 entry.setMessageType(*msgType);
248 qWarning() <<
"Unknown msgType field" << elementText;
253 const auto elementText = m_xml.readElementText();
254 const auto scope = stringToValue(elementText, scope_map);
256 entry.setScope(*scope);
258 qWarning() <<
"Unknown scope field" << elementText;
263 entry.setNote(m_xml.readElementText());
266 auto info = parseInfo();
267 entry.addInfo(std::move(info));
270 case Tags::REFERENCES:
271 entry.setReferences(parseReferences(m_xml.readElementText()));
274 m_xml.skipCurrentElement();
280CAPAlertInfo CAPParser::parseInfo()
285 while (!m_xml.atEnd() && !(m_xml.isEndElement() && m_xml.name() ==
QLatin1String(
"info"))) {
287 if (!m_xml.isStartElement()) {
290 const auto tag = stringToValue(m_xml.name(), info_tag_map);
293 case InfoTags::CATEGORY: {
294 const auto s = m_xml.readElementText();
295 const auto category = stringToValue(s, category_map);
297 info.addCategory(*category);
301 case InfoTags::EVENT:
302 info.setEvent(m_xml.readElementText());
304 case InfoTags::URGENCY: {
305 const auto s = m_xml.readElementText();
306 if (
const auto urgency = stringToValue(s, urgency_map); urgency) {
307 info.setUrgency(*urgency);
309 qWarning() <<
"Unknown urgency type:" << s;
313 case InfoTags::SEVERITY: {
314 const auto s = m_xml.readElementText();
315 if (
const auto severity = stringToValue(s, severity_map); severity) {
316 info.setSeverity(*severity);
318 qWarning() <<
"Unknown severity type:" << s;
322 case InfoTags::CERTAINITY: {
323 const auto s = m_xml.readElementText();
324 if (
const auto certainty = stringToValue(s, certainty_map); certainty) {
325 info.setCertainty(*certainty);
327 qWarning() <<
"Unknown certainty type:" << s;
331 case InfoTags::EFFECTIVE_TIME:
334 case InfoTags::ONSET_TIME:
337 case InfoTags::EXPIRE_TIME:
340 case InfoTags::HEADLINE:
341 info.setHeadline(m_xml.readElementText());
343 case InfoTags::DESCRIPTION:
344 info.setDescription(m_xml.readElementText());
346 case InfoTags::INSTRUCTION:
347 info.setInstruction(m_xml.readElementText());
349 case InfoTags::PARAMETER: {
350 info.addParameter(parseNamedValue());
353 case InfoTags::AREA: {
354 info.addArea(parseArea());
357 case InfoTags::SENDERNAME: {
358 info.setSender(m_xml.readElementText());
361 case InfoTags::LANGUAGE:
362 info.setLanguage(m_xml.readElementText());
364 case InfoTags::RESPONSETYPE: {
365 const auto elementText = m_xml.readElementText();
366 if (
const auto respType = stringToValue(elementText, response_type_map)) {
367 info.addResponseType(*respType);
369 qWarning() <<
"Unknown respone type value" << elementText;
373 case InfoTags::CONTACT:
374 info.setContact(m_xml.readElementText());
377 info.setWeb(m_xml.readElementText());
379 case InfoTags::EVENTCODE:
380 info.addEventCode(parseNamedValue());
384 if (m_xml.isStartElement()) {
385 qWarning() <<
"unknown element: " << m_xml.name();
393CAPArea CAPParser::parseArea()
396 while (!(m_xml.isEndElement() && m_xml.name() ==
QLatin1String(
"area"))) {
398 if (m_xml.name() ==
QLatin1String(
"areaDesc") && !m_xml.isEndElement()) {
399 area.setDescription(m_xml.readElementText());
400 }
else if (m_xml.name() ==
QLatin1String(
"geocode") && !m_xml.isEndElement()) {
401 area.addGeoCode(parseNamedValue());
402 }
else if (m_xml.name() ==
QLatin1String(
"polygon") && !m_xml.isEndElement()) {
403 area.addPolygon(stringToPolygon(m_xml.readElementText()));
404 }
else if (m_xml.name() ==
QLatin1String(
"circle") && !m_xml.isEndElement()) {
405 const auto t = m_xml.readElementText();
408 if (commaIdx > 0 && spaceIdx > commaIdx && commaIdx < t.size()) {
413 area.addCircle(std::move(circle));
415 }
else if (m_xml.name() ==
QLatin1String(
"altitude") && !m_xml.isEndElement()) {
416 area.setAltitude(m_xml.readElementText().toFloat());
417 }
else if (m_xml.name() ==
QLatin1String(
"ceiling") && !m_xml.isEndElement()) {
418 area.setCeiling(m_xml.readElementText().toFloat());
419 }
else if (m_xml.isStartElement()) {
420 qDebug() <<
"unknown area element:" << m_xml.name();
426CAPNamedValue CAPParser::parseNamedValue()
429 const auto elementName = m_xml.name().toString();
430 while (!m_xml.isEndElement() || m_xml.name() != elementName) {
432 if (m_xml.isStartElement() && m_xml.name() ==
QLatin1String(
"valueName")) {
433 value.name = m_xml.readElementText();
434 }
else if (m_xml.isStartElement() && m_xml.name() ==
QLatin1String(
"value")) {
435 value.value = m_xml.readElementText();
436 }
else if (m_xml.isStartElement()) {
437 qDebug() <<
"unknown named value element:" << m_xml.name();
443std::vector<CAPReference> CAPParser::parseReferences(
const QString &refsString)
445 std::vector<CAPReference> refs;
448 refs.
reserve(refsSplit.size());
449 for (
const auto &refString : refsSplit) {
450 const auto refSplit = refString.split(
QLatin1Char(
','));
451 if (refSplit.size() != 3) {
452 qDebug() <<
"failed to parse CAP reference:" << refString;
@ Test
Technical testing only, all recipients disregard.
@ Actual
Actionable by all targeted recipients.
@ Exercise
Actionable only by designated exercise participants.
@ Draft
A preliminary template or draft, not actionable in its current form.
@ Public
For general dissemination to unrestricted audiences.
@ Private
For dissemination only to specified addresses.
@ Restricted
For dissemination only to users with a known operational requirement.
@ Update
Updates and supercedes the earlier message(s) identified in references()
@ Error
Indicates rejection of the message(s) identified in references()
@ Alert
Initial information requiring attention by targeted recipients.
@ Acknowledge
Acknowledges receipt and acceptance of the message(s) identified in references()
@ Cancel
Cancels the earlier message(s) identified in references()
Q_SCRIPTABLE CaptureState status()
Category category(StandardShortcut id)
bool isEmpty() const const
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
void reserve(qsizetype size)
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QStringView left(qsizetype length) const const
QStringView mid(qsizetype start, qsizetype length) const const
float toFloat(bool *ok) const const