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