File indexing completed on 2024-11-24 04:15:41

0001 /*
0002     SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "mapcsselementstate.h"
0008 #include "mapcssselector_p.h"
0009 #include "mapcsscondition_p.h"
0010 #include "mapcssresult.h"
0011 #include "mapcssstate_p.h"
0012 
0013 #include <QDebug>
0014 #include <QIODevice>
0015 
0016 #include <cmath>
0017 #include <cstring>
0018 
0019 using namespace KOSMIndoorMap;
0020 
0021 MapCSSSelector::MapCSSSelector() = default;
0022 MapCSSSelector::~MapCSSSelector() = default;
0023 
0024 MapCSSBasicSelector::MapCSSBasicSelector() = default;
0025 MapCSSBasicSelector::~MapCSSBasicSelector() = default;
0026 
0027 void MapCSSBasicSelector::compile(const OSM::DataSet &dataSet)
0028 {
0029     for (const auto &c : conditions) {
0030         c->compile(dataSet);
0031     }
0032 }
0033 
0034 bool MapCSSBasicSelector::matches(const MapCSSState &state, MapCSSResult &result, const std::vector<std::unique_ptr<MapCSSDeclaration>> &declarations) const
0035 {
0036     // check object type
0037     switch (m_objectType) {
0038         case MapCSSObjectType::Node: if (state.element.type() != OSM::Type::Node) return false; break;
0039         case MapCSSObjectType::Way: if (state.element.type() != OSM::Type::Way) return false; break;
0040         case MapCSSObjectType::Relation: if (state.element.type() != OSM::Type::Relation) return false; break;
0041         case MapCSSObjectType::Area: if (state.objectType !=MapCSSObjectType::Area && state.objectType != MapCSSObjectType::LineOrArea) return false; break;
0042         case MapCSSObjectType::Line: if (state.objectType !=MapCSSObjectType::Line && state.objectType != MapCSSObjectType::LineOrArea) return false; break;
0043         case MapCSSObjectType::Canvas: return false;
0044         case MapCSSObjectType::Any: break;
0045         case MapCSSObjectType::LineOrArea: Q_UNREACHABLE();
0046     }
0047 
0048     // check zoom level
0049     if (m_zoomLow > 0 && state.zoomLevel < m_zoomLow) {
0050         return false;
0051     }
0052     if (m_zoomHigh > 0 && state.zoomLevel >= m_zoomHigh) {
0053         return false;
0054     }
0055 
0056     if (!m_class.isNull() && !result[m_layer].hasClass(m_class)) {
0057         return false;
0058     }
0059 
0060     if (m_elementState && (state.state & m_elementState) == 0) {
0061         return false;
0062     }
0063 
0064     if (std::all_of(conditions.begin(), conditions.end(), [&state](const auto &cond) { return cond->matches(state); })) {
0065         result.applyDeclarations(m_layer, declarations);
0066         return true;
0067     }
0068     return false;
0069 }
0070 
0071 bool MapCSSBasicSelector::matchesCanvas(const MapCSSState &state) const
0072 {
0073     if (m_objectType != MapCSSObjectType::Canvas) {
0074         return false;
0075     }
0076 
0077     if (m_zoomLow > 0 && state.zoomLevel < m_zoomLow) {
0078         return false;
0079     }
0080     if (m_zoomHigh > 0 && state.zoomLevel >= m_zoomHigh) {
0081         return false;
0082     }
0083 
0084     return std::all_of(conditions.begin(), conditions.end(), [&state](const auto &cond) { return cond->matchesCanvas(state); });
0085 }
0086 
0087 LayerSelectorKey MapCSSBasicSelector::layerSelector() const
0088 {
0089     return m_layer;
0090 }
0091 
0092 struct {
0093     const char *name;
0094     MapCSSObjectType type;
0095 } static constexpr const object_type_map[] = {
0096     { "node", MapCSSObjectType::Node },
0097     { "way", MapCSSObjectType::Way },
0098     { "relation", MapCSSObjectType::Relation },
0099     { "area", MapCSSObjectType::Area },
0100     { "line", MapCSSObjectType::Line },
0101     { "canvas", MapCSSObjectType::Canvas },
0102     { "*", MapCSSObjectType::Any },
0103 };
0104 
0105 void MapCSSBasicSelector::write(QIODevice *out) const
0106 {
0107     for (const auto &t : object_type_map) {
0108         if (m_objectType == t.type) {
0109             out->write(t.name);
0110             break;
0111         }
0112     }
0113 
0114     if (!m_class.isNull()) {
0115         out->write(".");
0116         out->write(m_class.name());
0117     }
0118 
0119     if (m_zoomLow > 0 || m_zoomHigh > 0) {
0120         out->write("|z");
0121         if (m_zoomLow == m_zoomHigh) {
0122             out->write(QByteArray::number(m_zoomLow));
0123         } else {
0124             if (m_zoomLow > 0) {
0125                 out->write(QByteArray::number(m_zoomLow));
0126             }
0127             out->write("-");
0128             if (m_zoomHigh > 0) {
0129                 out->write(QByteArray::number(m_zoomHigh));
0130             }
0131         }
0132     }
0133 
0134     for (const auto &cond : conditions) {
0135         cond->write(out);
0136     }
0137 
0138     if (m_elementState & MapCSSElementState::Active) {
0139         out->write(":active");
0140     }
0141     if (m_elementState & MapCSSElementState::Hovered) {
0142         out->write(":hovered");
0143     }
0144 
0145     if (!m_layer.isNull()) {
0146         out->write("::");
0147         out->write(m_layer.name());
0148     }
0149 }
0150 
0151 void MapCSSBasicSelector::setObjectType(const char *str, std::size_t len)
0152 {
0153     for (const auto &t : object_type_map) {
0154         if (std::strncmp(t.name, str, std::max(std::strlen(t.name), len)) == 0) {
0155             m_objectType = t.type;
0156             return;
0157         }
0158     }
0159 }
0160 
0161 void MapCSSBasicSelector::setZoomRange(int low, int high)
0162 {
0163     m_zoomLow = low;
0164     m_zoomHigh = high;
0165 }
0166 
0167 void MapCSSBasicSelector::setConditions(MapCSSConditionHolder *conds)
0168 {
0169     if (!conds) {
0170         return;
0171     }
0172     conditions = std::move(conds->conditions);
0173     delete conds;
0174 }
0175 
0176 void MapCSSBasicSelector::setClass(ClassSelectorKey key)
0177 {
0178     m_class = key;
0179 }
0180 
0181 void MapCSSBasicSelector::setPseudoClass(const char *str, std::size_t len)
0182 {
0183     if (std::strncmp(str, "active", len) == 0) {
0184         m_elementState |= MapCSSElementState::Active;
0185     } else if (std::strncmp(str, "hovered", len) == 0) {
0186         m_elementState |= MapCSSElementState::Hovered;
0187     } else {
0188         qWarning() << "Unhandled pseudo class:" << QByteArrayView(str, (qsizetype)len);
0189     }
0190 }
0191 
0192 void MapCSSBasicSelector::setLayer(LayerSelectorKey key)
0193 {
0194     m_layer = key;
0195 }
0196 
0197 
0198 void MapCSSChainedSelector::compile(const OSM::DataSet &dataSet)
0199 {
0200     for (const auto &s : selectors) {
0201         s->compile(dataSet);
0202     }
0203 }
0204 
0205 bool MapCSSChainedSelector::matches([[maybe_unused]] const MapCSSState &state, [[maybe_unused]] MapCSSResult &result,
0206                                     [[maybe_unused]] const std::vector<std::unique_ptr<MapCSSDeclaration>> &declarations) const
0207 {
0208     // TODO
0209     return false;
0210 }
0211 
0212 bool MapCSSChainedSelector::matchesCanvas(const MapCSSState &state) const
0213 {
0214     Q_UNUSED(state);
0215     // canvas cannot be chained
0216     return false;
0217 }
0218 
0219 LayerSelectorKey MapCSSChainedSelector::layerSelector() const
0220 {
0221     return selectors.back()->layerSelector();
0222 }
0223 
0224 void MapCSSChainedSelector::write(QIODevice *out) const
0225 {
0226     assert(selectors.size() > 1);
0227     selectors[0]->write(out);
0228     for (auto it = std::next(selectors.begin()); it != selectors.end(); ++it) {
0229         out->write(" ");
0230         (*it)->write(out);
0231     }
0232 }
0233 
0234 
0235 MapCSSUnionSelector::MapCSSUnionSelector() = default;
0236 MapCSSUnionSelector::~MapCSSUnionSelector() = default;
0237 
0238 void MapCSSUnionSelector::compile(const OSM::DataSet &dataSet)
0239 {
0240     for (const auto &ls : m_selectors) {
0241         for (const auto &s : ls.selectors) {
0242             s->compile(dataSet);
0243         }
0244     }
0245 }
0246 
0247 bool MapCSSUnionSelector::matches(const MapCSSState &state, MapCSSResult &result, const std::vector<std::unique_ptr<MapCSSDeclaration>> &declarations) const
0248 {
0249     bool ret = false;
0250     for (const auto &ls : m_selectors) {
0251         if (std::any_of(ls.selectors.begin(), ls.selectors.end(), [&state, &result, &declarations](const auto &selector) {
0252             return selector->matches(state, result, declarations);
0253 
0254         })) {
0255             // no short-circuit evaluation, we want the matchCallback to be called once per matching layer!
0256             ret = true;
0257         }
0258     }
0259     return ret;
0260 }
0261 
0262 bool MapCSSUnionSelector::matchesCanvas(const MapCSSState &state) const
0263 {
0264     for (const auto &ls : m_selectors) {
0265         if (ls.layer.isNull()) {
0266             return std::any_of(ls.selectors.begin(), ls.selectors.end(), [&state](const auto &selector) { return selector->matchesCanvas(state); });
0267         }
0268     }
0269     return false;
0270 }
0271 
0272 LayerSelectorKey MapCSSUnionSelector::layerSelector() const
0273 {
0274     return {};
0275 }
0276 
0277 void MapCSSUnionSelector::write(QIODevice *out) const
0278 {
0279     for (std::size_t i = 0; i < m_selectors.size(); ++i) {
0280         for (std::size_t j = 0; j < m_selectors[i].selectors.size(); ++j) {
0281             if (i != 0 || j != 0) {
0282                 out->write(",\n");
0283             }
0284             m_selectors[i].selectors[j]->write(out);
0285         }
0286     }
0287 }
0288 
0289 void MapCSSUnionSelector::addSelector(std::unique_ptr<MapCSSSelector> &&selector)
0290 {
0291     auto it = std::find_if(m_selectors.begin(), m_selectors.end(), [&selector](const auto &ls) {
0292         return ls.layer == selector->layerSelector();
0293     });
0294     if (it != m_selectors.end()) {
0295         (*it).selectors.push_back(std::move(selector));
0296     } else {
0297         SelectorMap ls;
0298         ls.layer = selector->layerSelector();
0299         ls.selectors.push_back(std::move(selector));
0300         m_selectors.push_back(std::move(ls));
0301     }
0302 }