File indexing completed on 2024-11-24 04:15:40
0001 /* 0002 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "mapcsscondition_p.h" 0008 #include "mapcssstate_p.h" 0009 0010 #include <QDebug> 0011 #include <QIODevice> 0012 0013 using namespace KOSMIndoorMap; 0014 0015 MapCSSCondition::MapCSSCondition() = default; 0016 MapCSSCondition::MapCSSCondition(MapCSSCondition &&) = default; 0017 MapCSSCondition::~MapCSSCondition() = default; 0018 0019 static double toNumber(const QByteArray &val) 0020 { 0021 bool res = false; 0022 const auto n = val.toDouble(&res); 0023 return res ? n : NAN; 0024 } 0025 0026 void MapCSSCondition::compile(const OSM::DataSet &dataSet) 0027 { 0028 if (m_key == "mx:closed") { 0029 m_tagKey = dataSet.tagKey("opening_hours"); 0030 m_op = (m_op == KeyNotSet ? IsNotClosed : IsClosed); 0031 } else { 0032 m_tagKey = dataSet.tagKey(m_key.constData()); 0033 } 0034 0035 switch(m_op) { 0036 case KeySet: 0037 case KeyNotSet: 0038 break; 0039 case Equal: 0040 case NotEqual: 0041 if (m_value.isEmpty() && std::isnan(m_numericValue)) { 0042 qWarning() << "Empty comparison, use key (not) set operation instead!"; 0043 } 0044 break; 0045 case LessThan: 0046 case GreaterThan: 0047 case LessOrEqual: 0048 case GreaterOrEqual: 0049 if (std::isnan(m_numericValue)) { 0050 qWarning() << "Numeric comparison without numeric value set!"; 0051 } 0052 break; 0053 case IsClosed: 0054 case IsNotClosed: 0055 break; 0056 } 0057 } 0058 0059 bool MapCSSCondition::matches(const MapCSSState &state) const 0060 { 0061 if (m_tagKey.isNull()) { 0062 // if we couldn't compile the tag, it doesn't exist and thus can never be set 0063 return m_op == KeyNotSet || m_op == NotEqual; 0064 } 0065 0066 // this method is such a hot path that even the ref/deref in QByteArray for OSM::Element::tagValue matters 0067 // so we do tag lookup manually here 0068 const auto tagEnd = state.element.tagsEnd(); 0069 const auto tagIt = std::lower_bound(state.element.tagsBegin(), tagEnd, m_tagKey); 0070 const auto tagIsSet = (tagIt != tagEnd && (*tagIt).key == m_tagKey); 0071 switch (m_op) { 0072 case KeySet: 0073 return tagIsSet; 0074 case KeyNotSet: 0075 return !tagIsSet; 0076 case Equal: 0077 if (std::isnan(m_numericValue)) { 0078 return !tagIsSet ? false : (*tagIt).value == m_value; 0079 } 0080 return !tagIsSet ? false : toNumber((*tagIt).value) == m_numericValue; 0081 case NotEqual: 0082 if (std::isnan(m_numericValue)) { 0083 return !tagIsSet ? true : (*tagIt).value != m_value; 0084 } 0085 return !tagIsSet ? true : toNumber((*tagIt).value) != m_numericValue; 0086 case LessThan: return !tagIsSet ? false : toNumber((*tagIt).value) < m_numericValue; 0087 case GreaterThan: return !tagIsSet ? false : toNumber((*tagIt).value) > m_numericValue; 0088 case LessOrEqual: return !tagIsSet ? false : toNumber((*tagIt).value) <= m_numericValue; 0089 case GreaterOrEqual: return !tagIsSet ? false : toNumber((*tagIt).value) >= m_numericValue; 0090 case IsClosed: 0091 case IsNotClosed: 0092 { 0093 if (!tagIsSet || (*tagIt).value.isEmpty() || !state.openingHours) { 0094 return m_op == IsNotClosed; 0095 } 0096 const auto closed = state.openingHours->isClosed(state.element, (*tagIt).value); 0097 return m_op == IsClosed ? closed : !closed; 0098 } 0099 } 0100 return false; 0101 } 0102 0103 bool MapCSSCondition::matchesCanvas(const MapCSSState &state) const 0104 { 0105 if (m_key != "level") { 0106 return false; 0107 } 0108 0109 switch (m_op) { 0110 case KeySet: 0111 case KeyNotSet: 0112 case IsClosed: 0113 case IsNotClosed: 0114 return false; 0115 case Equal: return (state.floorLevel/10) == m_numericValue; 0116 case NotEqual: return (state.floorLevel/10) != m_numericValue; 0117 case LessThan: return (state.floorLevel/10) < m_numericValue; 0118 case GreaterThan: return (state.floorLevel/10) > m_numericValue; 0119 case LessOrEqual: return (state.floorLevel/10) <= m_numericValue; 0120 case GreaterOrEqual: return (state.floorLevel/10) >= m_numericValue; 0121 } 0122 0123 return false; 0124 } 0125 0126 void MapCSSCondition::setKey(const char *key, int len) 0127 { 0128 m_key = QByteArray(key, len); 0129 } 0130 0131 void MapCSSCondition::setOperation(MapCSSCondition::Operator op) 0132 { 0133 m_op = op; 0134 } 0135 0136 void MapCSSCondition::setValue(const char *value, int len) 0137 { 0138 m_value = QByteArray(value, len); 0139 } 0140 0141 void MapCSSCondition::setValue(double val) 0142 { 0143 m_numericValue = val; 0144 } 0145 0146 void MapCSSCondition::write(QIODevice *out) const 0147 { 0148 out->write("["); 0149 if (m_op == KeyNotSet) { out->write("!"); } 0150 out->write(m_key); 0151 0152 switch (m_op) { 0153 case KeySet: 0154 case KeyNotSet: 0155 case IsClosed: 0156 case IsNotClosed: 0157 out->write("]"); return; 0158 case Equal: out->write("="); break; 0159 case NotEqual: out->write("!="); break; 0160 case LessThan: out->write("<"); break; 0161 case GreaterThan: out->write(">"); break; 0162 case LessOrEqual: out->write("<="); break; 0163 case GreaterOrEqual: out->write(">="); break; 0164 } 0165 0166 if (m_numericValue != NAN && m_value.isEmpty()) { 0167 out->write(QByteArray::number(m_numericValue)); 0168 } else { 0169 // TODO quote if m_value contains non-identifier chars 0170 out->write(m_value); 0171 } 0172 0173 out->write("]"); 0174 } 0175 0176 0177 void MapCSSConditionHolder::addCondition(MapCSSCondition *condition) 0178 { 0179 conditions.push_back(std::unique_ptr<MapCSSCondition>(condition)); 0180 }