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) {
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},
161static constexpr const MapEntry<CAPAlertInfo::Severity> severity_map[] = {
162 {
"Extreme", CAPAlertInfo::Severity::Extreme},
163 {
"Minor", CAPAlertInfo::Severity::Minor},
164 {
"Moderate", CAPAlertInfo::Severity::Moderate},
165 {
"Severe", CAPAlertInfo::Severity::Severe},
168static constexpr const MapEntry<CAPAlertInfo::Certainty> certainty_map[] = {
169 {
"Likely", CAPAlertInfo::Certainty::Likely},
170 {
"Observed", CAPAlertInfo::Certainty::Observed},
171 {
"Possible", CAPAlertInfo::Certainty::Possible},
172 {
"Unlikely", CAPAlertInfo::Certainty::Unlikely},
175[[nodiscard]]
static CAPPolygon stringToPolygon(
QStringView str)
180 const auto idx = coordinate.indexOf(
','_L1);
184 bool latOk =
false, lonOk =
false;
185 res.push_back({coordinate.left(idx).toFloat(&latOk), coordinate.mid(idx + 1).toFloat(&lonOk)});
186 if (!latOk || !lonOk) {
198 while (m_xml.readNextStartElement()) {
199 if (m_xml.name() == QStringLiteral(
"alert")) {
205 qWarning() <<
"Not a CAP XML";
210CAPAlertMessage CAPParser::parse()
212 CAPAlertMessage entry;
213 while (m_xml.readNextStartElement()) {
214 const auto tag = stringToValue(m_xml.name(), tag_map);
216 m_xml.skipCurrentElement();
220 case Tags::IDENTIFIER:
221 entry.setIdentifier(m_xml.readElementText());
224 entry.setSender(m_xml.readElementText());
226 case Tags::SENT_TIME:
230 const auto elementText = m_xml.readElementText();
231 const auto status = stringToValue(elementText, status_map);
235 qWarning() <<
"Unknown status field" << elementText;
239 case Tags::MSG_TYPE: {
240 const auto elementText = m_xml.readElementText();
241 const auto msgType = stringToValue(elementText, msgtype_map);
243 entry.setMessageType(*msgType);
245 qWarning() <<
"Unknown msgType field" << elementText;
250 const auto elementText = m_xml.readElementText();
251 const auto scope = stringToValue(elementText, scope_map);
253 entry.setScope(*scope);
255 qWarning() <<
"Unknown scope field" << elementText;
260 entry.setNote(m_xml.readElementText());
263 auto info = parseInfo();
264 entry.addInfo(std::move(info));
267 case Tags::REFERENCES:
268 entry.setReferences(parseReferences(m_xml.readElementText()));
271 m_xml.skipCurrentElement();
277CAPAlertInfo CAPParser::parseInfo()
282 while (!m_xml.atEnd() && !(m_xml.isEndElement() && m_xml.name() ==
QLatin1String(
"info"))) {
284 if (!m_xml.isStartElement()) {
287 const auto tag = stringToValue(m_xml.name(), info_tag_map);
290 case InfoTags::CATEGORY: {
291 const auto s = m_xml.readElementText();
292 const auto category = stringToValue(s, category_map);
294 info.addCategory(*category);
298 case InfoTags::EVENT:
299 info.setEvent(m_xml.readElementText());
301 case InfoTags::URGENCY: {
302 const auto s = m_xml.readElementText();
303 if (
const auto urgency = stringToValue(s, urgency_map); urgency) {
304 info.setUrgency(*urgency);
306 qWarning() <<
"Unknown urgency type:" << s;
310 case InfoTags::SEVERITY: {
311 const auto s = m_xml.readElementText();
312 if (
const auto severity = stringToValue(s, severity_map); severity) {
313 info.setSeverity(*severity);
315 qWarning() <<
"Unknown severity type:" << s;
319 case InfoTags::CERTAINITY: {
320 const auto s = m_xml.readElementText();
321 if (
const auto certainty = stringToValue(s, certainty_map); certainty) {
322 info.setCertainty(*certainty);
324 qWarning() <<
"Unknown certainty type:" << s;
328 case InfoTags::EFFECTIVE_TIME:
331 case InfoTags::ONSET_TIME:
334 case InfoTags::EXPIRE_TIME:
337 case InfoTags::HEADLINE:
338 info.setHeadline(m_xml.readElementText());
340 case InfoTags::DESCRIPTION:
341 info.setDescription(m_xml.readElementText());
343 case InfoTags::INSTRUCTION:
344 info.setInstruction(m_xml.readElementText());
346 case InfoTags::PARAMETER: {
347 info.addParameter(parseNamedValue());
350 case InfoTags::AREA: {
351 info.addArea(parseArea());
354 case InfoTags::SENDERNAME: {
355 info.setSender(m_xml.readElementText());
358 case InfoTags::LANGUAGE:
359 info.setLanguage(m_xml.readElementText());
361 case InfoTags::RESPONSETYPE: {
362 const auto elementText = m_xml.readElementText();
363 if (
const auto respType = stringToValue(elementText, response_type_map)) {
364 info.addResponseType(*respType);
366 qWarning() <<
"Unknown respone type value" << elementText;
370 case InfoTags::CONTACT:
371 info.setContact(m_xml.readElementText());
374 info.setWeb(m_xml.readElementText());
376 case InfoTags::EVENTCODE:
377 info.addEventCode(parseNamedValue());
381 if (m_xml.isStartElement()) {
382 qWarning() <<
"unknown element: " << m_xml.name();
390CAPArea CAPParser::parseArea()
393 while (!(m_xml.isEndElement() && m_xml.name() ==
QLatin1String(
"area"))) {
395 if (m_xml.name() ==
QLatin1String(
"areaDesc") && !m_xml.isEndElement()) {
396 area.setDescription(m_xml.readElementText());
397 }
else if (m_xml.name() ==
QLatin1String(
"geocode") && !m_xml.isEndElement()) {
398 area.addGeoCode(parseNamedValue());
399 }
else if (m_xml.name() ==
QLatin1String(
"polygon") && !m_xml.isEndElement()) {
400 area.addPolygon(stringToPolygon(m_xml.readElementText()));
401 }
else if (m_xml.name() ==
QLatin1String(
"circle") && !m_xml.isEndElement()) {
402 const auto t = m_xml.readElementText();
405 if (commaIdx > 0 && spaceIdx > commaIdx && commaIdx < t.size()) {
410 area.addCircle(std::move(circle));
412 }
else if (m_xml.name() ==
QLatin1String(
"altitude") && !m_xml.isEndElement()) {
413 area.setAltitude(m_xml.readElementText().toFloat());
414 }
else if (m_xml.name() ==
QLatin1String(
"ceiling") && !m_xml.isEndElement()) {
415 area.setCeiling(m_xml.readElementText().toFloat());
416 }
else if (m_xml.isStartElement()) {
417 qDebug() <<
"unknown area element:" << m_xml.name();
423CAPNamedValue CAPParser::parseNamedValue()
426 const auto elementName = m_xml.name().toString();
427 while (!m_xml.isEndElement() || m_xml.name() != elementName) {
429 if (m_xml.isStartElement() && m_xml.name() ==
QLatin1String(
"valueName")) {
430 value.name = m_xml.readElementText();
431 }
else if (m_xml.isStartElement() && m_xml.name() ==
QLatin1String(
"value")) {
432 value.value = m_xml.readElementText();
433 }
else if (m_xml.isStartElement()) {
434 qDebug() <<
"unknown named value element:" << m_xml.name();
440std::vector<CAPReference> CAPParser::parseReferences(
const QString &refsString)
442 std::vector<CAPReference> refs;
445 refs.
reserve(refsSplit.size());
446 for (
const auto &refString : refsSplit) {
447 const auto refSplit = refString.split(
QLatin1Char(
','));
448 if (refSplit.size() != 3) {
449 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