8#include "capalertinfo.h"
9#include "capalertmessage.h"
11#include "capnamedvalue.h"
12#include "capreference.h"
13#include "kweathercore_p.h"
14#include <KLocalizedString>
29template<
typename QStringT,
typename EnumT, std::
size_t N>
30static std::optional<EnumT> stringToValue(
const QStringT &s,
const MapEntry<EnumT> (&map)[N])
32 const auto it = std::lower_bound(std::begin(map), std::end(map), s, [](
auto lhs,
auto rhs) {
42static constexpr const MapEntry<CAPAlertInfo::Category> category_map[] = {
43 {
"CBRNE", CAPAlertInfo::Category::CBRNE},
44 {
"Env", CAPAlertInfo::Category::Environmental},
45 {
"Fire", CAPAlertInfo::Category::Fire},
46 {
"Geo", CAPAlertInfo::Category::Geophysical},
47 {
"Health", CAPAlertInfo::Category::Health},
48 {
"Infra", CAPAlertInfo::Category::Infrastructure},
49 {
"Met", CAPAlertInfo::Category::Meteorological},
50 {
"Other", CAPAlertInfo::Category::Other},
51 {
"Rescue", CAPAlertInfo::Category::Rescue},
52 {
"Safety", CAPAlertInfo::Category::Safety},
53 {
"Security", CAPAlertInfo::Category::Security},
54 {
"Transport", CAPAlertInfo::Category::Transport},
57enum class Tags { ALERT, IDENTIFIER, SENDER, SENT_TIME, STATUS, MSG_TYPE, SCOPE, NOTE, INFO, REFERENCES };
59static constexpr const MapEntry<Tags> tag_map[] = {
60 {
"alert", Tags::ALERT},
61 {
"identifier", Tags::IDENTIFIER},
63 {
"msgType", Tags::MSG_TYPE},
65 {
"references", Tags::REFERENCES},
66 {
"scope", Tags::SCOPE},
67 {
"sender", Tags::SENDER},
68 {
"sent", Tags::SENT_TIME},
69 {
"status", Tags::STATUS},
94static constexpr const MapEntry<InfoTags> info_tag_map[] = {
95 {
"area", InfoTags::AREA},
96 {
"category", InfoTags::CATEGORY},
97 {
"certainty", InfoTags::CERTAINITY},
98 {
"contact", InfoTags::CONTACT},
99 {
"description", InfoTags::DESCRIPTION},
100 {
"effective", InfoTags::EFFECTIVE_TIME},
101 {
"event", InfoTags::EVENT},
102 {
"eventCode", InfoTags::EVENTCODE},
103 {
"expires", InfoTags::EXPIRE_TIME},
104 {
"headline", InfoTags::HEADLINE},
105 {
"instruction", InfoTags::INSTRUCTION},
106 {
"language", InfoTags::LANGUAGE},
107 {
"onset", InfoTags::ONSET_TIME},
108 {
"parameter", InfoTags::PARAMETER},
109 {
"responseType", InfoTags::RESPONSETYPE},
110 {
"senderName", InfoTags::SENDERNAME},
111 {
"severity", InfoTags::SEVERITY},
112 {
"urgency", InfoTags::URGENCY},
113 {
"web", InfoTags::WEB},
116static constexpr const MapEntry<CAPAlertMessage::Status> status_map[] = {
120 {
"System", CAPAlertMessage::Status::System},
124static constexpr const MapEntry<CAPAlertMessage::MessageType> msgtype_map[] = {
132static constexpr const MapEntry<CAPAlertMessage::Scope> scope_map[] = {
138static constexpr const MapEntry<CAPAlertInfo::ResponseType> response_type_map[] = {
139 {
"AllClear", CAPAlertInfo::ResponseType::AllClear},
140 {
"Assess", CAPAlertInfo::ResponseType::Assess},
141 {
"Avoid", CAPAlertInfo::ResponseType::Avoid},
142 {
"Evacuate", CAPAlertInfo::ResponseType::Evacuate},
143 {
"Execute", CAPAlertInfo::ResponseType::Execute},
144 {
"Monitor", CAPAlertInfo::ResponseType::Monitor},
145 {
"None", CAPAlertInfo::ResponseType::None},
146 {
"Prepare", CAPAlertInfo::ResponseType::Prepare},
147 {
"Shelter", CAPAlertInfo::ResponseType::Shelter},
155 while (m_xml.readNextStartElement()) {
156 if (m_xml.name() == QStringLiteral(
"alert")) {
162 qWarning() <<
"Not a CAP XML";
167CAPAlertMessage CAPParser::parse()
169 CAPAlertMessage entry;
170 while (m_xml.readNextStartElement()) {
171 const auto tag = stringToValue(m_xml.name(), tag_map);
173 m_xml.skipCurrentElement();
177 case Tags::IDENTIFIER:
178 entry.setIdentifier(m_xml.readElementText());
181 entry.setSender(m_xml.readElementText());
183 case Tags::SENT_TIME:
187 const auto elementText = m_xml.readElementText();
188 const auto status = stringToValue(elementText, status_map);
192 qWarning() <<
"Unknown status field" << elementText;
196 case Tags::MSG_TYPE: {
197 const auto elementText = m_xml.readElementText();
198 const auto msgType = stringToValue(elementText, msgtype_map);
200 entry.setMessageType(*msgType);
202 qWarning() <<
"Unknown msgType field" << elementText;
207 const auto elementText = m_xml.readElementText();
208 const auto scope = stringToValue(elementText, scope_map);
210 entry.setScope(*scope);
212 qWarning() <<
"Unknown scope field" << elementText;
217 entry.setNote(m_xml.readElementText());
220 auto info = parseInfo();
221 entry.addInfo(std::move(info));
224 case Tags::REFERENCES:
225 entry.setReferences(parseReferences(m_xml.readElementText()));
228 m_xml.skipCurrentElement();
234CAPAlertInfo CAPParser::parseInfo()
239 while (!m_xml.atEnd() && !(m_xml.isEndElement() && m_xml.name() ==
QLatin1String(
"info"))) {
241 if (!m_xml.isStartElement()) {
244 const auto tag = stringToValue(m_xml.name(), info_tag_map);
247 case InfoTags::CATEGORY: {
248 const auto s = m_xml.readElementText();
249 const auto category = stringToValue(s, category_map);
251 info.addCategory(*category);
255 case InfoTags::EVENT:
256 info.setEvent(m_xml.readElementText());
258 case InfoTags::URGENCY:
259 info.setUrgency(KWeatherCorePrivate::urgencyStringToEnum(m_xml.readElementText()));
261 case InfoTags::SEVERITY:
262 info.setSeverity(KWeatherCorePrivate::severityStringToEnum(m_xml.readElementText()));
264 case InfoTags::CERTAINITY:
265 info.setCertainty(KWeatherCorePrivate::certaintyStringToEnum(m_xml.readElementText()));
267 case InfoTags::EFFECTIVE_TIME:
270 case InfoTags::ONSET_TIME:
273 case InfoTags::EXPIRE_TIME:
276 case InfoTags::HEADLINE:
277 info.setHeadline(m_xml.readElementText());
279 case InfoTags::DESCRIPTION:
280 info.setDescription(m_xml.readElementText());
282 case InfoTags::INSTRUCTION:
283 info.setInstruction(m_xml.readElementText());
285 case InfoTags::PARAMETER: {
286 info.addParameter(parseNamedValue());
289 case InfoTags::AREA: {
290 info.addArea(parseArea());
293 case InfoTags::SENDERNAME: {
294 info.setSender(m_xml.readElementText());
297 case InfoTags::LANGUAGE:
298 info.setLanguage(m_xml.readElementText());
300 case InfoTags::RESPONSETYPE: {
301 const auto elementText = m_xml.readElementText();
302 if (
const auto respType = stringToValue(elementText, response_type_map)) {
303 info.addResponseType(*respType);
305 qWarning() <<
"Unknown respone type value" << elementText;
309 case InfoTags::CONTACT:
310 info.setContact(m_xml.readElementText());
313 info.setWeb(m_xml.readElementText());
315 case InfoTags::EVENTCODE:
316 info.addEventCode(parseNamedValue());
320 if (m_xml.isStartElement()) {
321 qWarning() <<
"unknown element: " << m_xml.name();
329CAPArea CAPParser::parseArea()
332 while (!(m_xml.isEndElement() && m_xml.name() ==
QLatin1String(
"area"))) {
334 if (m_xml.name() ==
QLatin1String(
"areaDesc") && !m_xml.isEndElement()) {
335 area.setDescription(m_xml.readElementText());
336 }
else if (m_xml.name() ==
QLatin1String(
"geocode") && !m_xml.isEndElement()) {
337 area.addGeoCode(parseNamedValue());
338 }
else if (m_xml.name() ==
QLatin1String(
"polygon") && !m_xml.isEndElement()) {
339 area.addPolygon(KWeatherCorePrivate::stringToPolygon(m_xml.readElementText()));
340 }
else if (m_xml.name() ==
QLatin1String(
"circle") && !m_xml.isEndElement()) {
341 const auto t = m_xml.readElementText();
344 if (commaIdx > 0 && spaceIdx > commaIdx && commaIdx < t.size()) {
349 area.addCircle(std::move(circle));
351 }
else if (m_xml.name() ==
QLatin1String(
"altitude") && !m_xml.isEndElement()) {
352 area.setAltitude(m_xml.readElementText().toFloat());
353 }
else if (m_xml.name() ==
QLatin1String(
"ceiling") && !m_xml.isEndElement()) {
354 area.setCeiling(m_xml.readElementText().toFloat());
355 }
else if (m_xml.isStartElement()) {
356 qDebug() <<
"unknown area element:" << m_xml.name();
362CAPNamedValue CAPParser::parseNamedValue()
365 const auto elementName = m_xml.name().toString();
366 while (!m_xml.isEndElement() || m_xml.name() != elementName) {
368 if (m_xml.isStartElement() && m_xml.name() ==
QLatin1String(
"valueName")) {
369 value.name = m_xml.readElementText();
370 }
else if (m_xml.isStartElement() && m_xml.name() ==
QLatin1String(
"value")) {
371 value.value = m_xml.readElementText();
372 }
else if (m_xml.isStartElement()) {
373 qDebug() <<
"unknown named value element:" << m_xml.name();
379std::vector<CAPReference> CAPParser::parseReferences(
const QString &refsString)
381 std::vector<CAPReference> refs;
384 refs.
reserve(refsSplit.size());
385 for (
const auto &refString : refsSplit) {
386 const auto refSplit = refString.split(
QLatin1Char(
','));
387 if (refSplit.size() != 3) {
388 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