1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
|
#ifndef CSVREADER_HH
#define CSVREADER_HH
#include <QObject>
#include <QTextStream>
#include <QMap>
#include <QVector>
#include "channel.hh"
#include "contact.hh"
class Config;
class DMRContact;
class RXGroupList;
class Zone;
class PositioningSystem;
class ScanList;
class RoamingZone;
/** The lexer class divides a text stream into tokens. */
class CSVLexer: public QObject
{
Q_OBJECT
public:
/** The token. */
struct Token {
public:
/** Possible token types. */
enum TokenType {
T_KEYWORD, ///< A Keyword/Identifier.
T_APRSCALL, ///< A APRS call of form CALL-SSID.
T_STRING, ///< A quoted string.
T_NUMBER, ///< An integer or floating point number.
T_DCS_N, ///< A normal DCS code number.
T_DCS_I, ///< An inverted DCS code number.
T_COLON, ///< A colon.
T_NOT_SET, ///< A dash, being used as "not-set".
T_ENABLED, ///< A plus sign, being used as "enabled"
T_COMMA, ///< A comma
T_WHITESPACE, ///< Any whitespace character excluding newline.
T_NEWLINE, ///< A new line.
T_COMMENT, ///< A comment starts with # end ends at the line-end.
T_END_OF_STREAM, ///< Indicates the end-of-input.
T_ERROR ///< Indicates a lexer error.
};
/// The token type.
TokenType type;
/// The token value.
QString value;
/// Line number.
qint64 line;
/// Column number.
qint64 column;
};
/// Current state of lexer.
struct State {
/// The current stream offset.
qint64 offset;
/// The current line count.
qint64 line;
/// The current column number.
qint64 column;
};
public:
/** Constructs a lexer for the given stream. */
CSVLexer(QTextStream &stream, QObject *parent=nullptr);
/** Saves the current lexer state. */
void push();
/** Restores the last lexer state. */
void pop();
/** Reads the next token from the stream. */
Token next();
/** Returns the last error message. */
const QString &errorMessage() const;
protected:
/** Internal used function to get the next token. Also returns ignored tokens like whitespace
* and comment. */
Token lex();
protected:
/// The error message.
QString _errorMessage;
/// The text stream to read from.
QTextStream &_stream;
/// The stack of saved lexer states
QVector<State> _stack;
/// The current line count
QString _currentLine;
/// The list of patterns to match
static QVector< QPair<QRegularExpression, Token::TokenType> > _pattern;
};
/** Basic parse-handler interface.
*
* That is, a set of callbacks getting called by the parser on the occurrence of a particular
* statement in the config file.
* @ingroup conf */
class CSVHandler: public QObject
{
Q_OBJECT
protected:
/** Hidden constructor. */
explicit CSVHandler(QObject *parent=nullptr);
public:
/** Destructor. */
virtual ~CSVHandler();
/** Gets called once the DMR IDs has been parsed. */
virtual bool handleRadioId(const QList<qint64> &ids, qint64 line, qint64 column, QString &errorMessage);
/** Gets called once the radio name has been parsed. */
virtual bool handleRadioName(const QString &name, qint64 line, qint64 column, QString &errorMessage);
/** Gets called once the first intro line has been parsed. */
virtual bool handleIntroLine1(const QString &text, qint64 line, qint64 column, QString &errorMessage);
/** Gets called once the second intro line has been parsed. */
virtual bool handleIntroLine2(const QString &text, qint64 line, qint64 column, QString &errorMessage);
/** Gets called once the MIC level has been parsed. */
virtual bool handleMicLevel(unsigned level, qint64 line, qint64 column, QString &errorMessage);
/** Gets called once the Speech flag has been parsed. */
virtual bool handleSpeech(bool speech, qint64 line, qint64 column, QString &errorMessage);
/** Gets called once a DTMF contact has been parsed. */
virtual bool handleDTMFContact(qint64 idx, const QString &name, const QString &num, bool rxTone,
qint64 line, qint64 column, QString &errorMessage);
/** Gets called once a digital contact has been parsed. */
virtual bool handleDigitalContact(qint64 idx, const QString &name, DMRContact::Type type, qint64 id,
bool rxTone, qint64 line, qint64 column, QString &errorMessage);
/** Gets called once an RX group list has been parsed. */
virtual bool handleGroupList(qint64 idx, const QString &name, const QList<qint64> &contacts,
qint64 line, qint64 column, QString &errorMessage);
/** Gets called once a digital channel has been parsed. */
virtual bool handleDigitalChannel(qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan,
qint64 tot, bool ro, DMRChannel::Admit admit, qint64 color, DMRChannel::TimeSlot slot,
qint64 gl, qint64 contact, qint64 gps, qint64 roam, qint64 radioID, qint64 line, qint64 column, QString &errorMessage);
/** Gets called once a analog channel has been parsed. */
virtual bool handleAnalogChannel(qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan, qint64 aprs,
qint64 tot, bool ro, FMChannel::Admit admit, qint64 squelch, const SelectiveCall &rxTone, const SelectiveCall &txTone,
FMChannel::Bandwidth bw, qint64 line, qint64 column, QString &errorMessage);
/** Gets called once a zone list has been parsed. */
virtual bool handleZone(qint64 idx, const QString &name, bool a, const QList<qint64> &channels,
qint64 line, qint64 column, QString &errorMessage);
/** Gets called once a GPS system has been parsed. */
virtual bool handleGPSSystem(qint64 idx, const QString &name, qint64 contactIdx, qint64 period,
qint64 revertChannelIdx, qint64 line, qint64 column, QString &errorMessage);
/** Gets called once a APRS system has been parsed. */
virtual bool handleAPRSSystem(qint64 idx, const QString &name, qint64 channelIdx, qint64 period,
const QString &src, unsigned srcSSID, const QString &dest, unsigned destSSID,
const QString &path, const QString &icon, const QString &message,
qint64 line, qint64 column, QString &errorMessage);
/** Gets called once a scan list has been parsed. */
virtual bool handleScanList(qint64 idx, const QString &name, qint64 pch1, qint64 pch2, qint64 txch,
const QList<qint64> &channels, qint64 line, qint64 column, QString &errorMessage);
/** Gets called once a roaming zone list has been parsed. */
virtual bool handleRoamingZone(qint64 idx, const QString &name, const QList<qint64> &channels,
qint64 line, qint64 column, QString &errorMessage);
};
/** The actual config file parser.
*
* This class parses the config file and calls the associated callback functions of a handler
* instance that is responsible to assemble the final @c Config instance. */
class CSVParser: public QObject
{
Q_OBJECT
public:
/** Constructs a parser using the given handler instance. */
explicit CSVParser(CSVHandler *handler, QObject *parent=nullptr);
/** Parses the given text stream. */
bool parse(QTextStream &stream);
/** Returns the current error message, for example if @c parse returns @c false. */
const QString &errorMessage() const;
protected:
/** Internal function to parse DMR IDs. */
bool _parse_radio_id(CSVLexer &lexer);
/** Internal function to parse radio names. */
bool _parse_radio_name(CSVLexer &lexer);
/** Internal function to parse intro line 1. */
bool _parse_introline1(CSVLexer &lexer);
/** Internal function to parse intro line 2. */
bool _parse_introline2(CSVLexer &lexer);
/** Internal function to parse MIC level. */
bool _parse_mic_level(CSVLexer &lexer);
/** Internal function to parse Speech flag. */
bool _parse_speech(CSVLexer &lexer);
/** Internal function to parse UserDB flag. */
bool _parse_userdb(CSVLexer &lexer);
/** Internal function to parse a digital contact list. */
bool _parse_contacts(CSVLexer &lexer);
/** Internal function to parse digital contact. */
bool _parse_contact(qint64 id, CSVLexer &lexer);
/** Internal function to parse an RX group list. */
bool _parse_rx_groups(CSVLexer &lexer);
/** Internal function to parse an RX group. */
bool _parse_rx_group(qint64 id, CSVLexer &lexer);
/** Internal function to parse a digital channel list. */
bool _parse_digital_channels(CSVLexer &lexer);
/** Internal function to parse a digital channel. */
bool _parse_digital_channel(qint64 id, CSVLexer &lexer);
/** Internal function to parse an analog channel list. */
bool _parse_analog_channels(CSVLexer &lexer);
/** Internal function to parse an analog channel. */
bool _parse_analog_channel(qint64 id, CSVLexer &lexer);
/** Internal function to parse a zone list. */
bool _parse_zones(CSVLexer &lexer);
/** Internal function to parse a zone. */
bool _parse_zone(qint64 id, CSVLexer &lexer);
/** Internal function to parse a GPS system list. */
bool _parse_gps_systems(CSVLexer &lexer);
/** Internal function to parse a GPS system. */
bool _parse_gps_system(qint64 id, CSVLexer &lexer);
/** Internal function to parse a APRS system list. */
bool _parse_aprs_systems(CSVLexer &lexer);
/** Internal function to parse a APRS system. */
bool _parse_aprs_system(qint64 id, CSVLexer &lexer);
/** Internal function to parse a scanlist list. */
bool _parse_scanlists(CSVLexer &lexer);
/** Internal function to parse a scanlist. */
bool _parse_scanlist(qint64 id, CSVLexer &lexer);
/** Internal function to parse a roaming zone list. */
bool _parse_roaming_zones(CSVLexer &lexer);
/** Internal function to parse a zone. */
bool _parse_roaming_zone(qint64 id, CSVLexer &lexer);
protected:
/** Holds the current error message. */
QString _errorMessage;
/** The handler instance. */
CSVHandler *_handler;
};
/** Implements the text-file codeplug reader.
* @ingroup conf */
class CSVReader : public CSVHandler
{
Q_OBJECT
protected:
/** Hidden constructor. Consider using the static @c read method. */
CSVReader(Config *config, QObject *parent=nullptr);
public:
/** Reads a config file from @c stream and stores the read configuration into @c config.
* @returns @c true on success. */
static bool read(Config *config, QTextStream &stream, QString &errorMessage);
virtual bool handleRadioId(const QList<qint64> &ids, qint64 line, qint64 column, QString &errorMessage);
virtual bool handleRadioName(const QString &name, qint64 line, qint64 column, QString &errorMessage);
virtual bool handleIntroLine1(const QString &text, qint64 line, qint64 column, QString &errorMessage);
virtual bool handleIntroLine2(const QString &text, qint64 line, qint64 column, QString &errorMessage);
virtual bool handleMicLevel(unsigned level, qint64 line, qint64 column, QString &errorMessage);
virtual bool handleSpeech(bool speech, qint64 line, qint64 column, QString &errorMessage);
virtual bool handleDTMFContact(qint64 idx, const QString &name, const QString &num, bool rxTone,
qint64 line, qint64 column, QString &errorMessage);
virtual bool handleDigitalContact(
qint64 idx, const QString &name, DMRContact::Type type, qint64 id, bool rxTone,
qint64 line, qint64 column, QString &errorMessage);
virtual bool handleGroupList(qint64 idx, const QString &name, const QList<qint64> &contacts,
qint64 line, qint64 column, QString &errorMessage);
virtual bool handleDigitalChannel(
qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan,
qint64 tot, bool ro, DMRChannel::Admit admit, qint64 color, DMRChannel::TimeSlot slot,
qint64 gl, qint64 contact, qint64 gps, qint64 roam, qint64 radioID, qint64 line, qint64 column, QString &errorMessage);
virtual bool handleAnalogChannel(qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan, qint64 aprs,
qint64 tot, bool ro, FMChannel::Admit admit, qint64 squelch, const SelectiveCall &rxTone, const SelectiveCall &txTone,
FMChannel::Bandwidth bw, qint64 line, qint64 column, QString &errorMessage);
virtual bool handleZone(qint64 idx, const QString &name, bool a, const QList<qint64> &channels,
qint64 line, qint64 column, QString &errorMessage);
virtual bool handleGPSSystem(qint64 idx, const QString &name, qint64 contactIdx, qint64 period,
qint64 revertChannelIdx, qint64 line, qint64 column, QString &errorMessage);
virtual bool handleAPRSSystem(qint64 idx, const QString &name, qint64 channelIdx, qint64 period,
const QString &src, unsigned srcSSID, const QString &dest, unsigned destSSID, const QString &path,
const QString &icon, const QString &message,
qint64 line, qint64 column, QString &errorMessage);
virtual bool handleScanList(qint64 idx, const QString &name, qint64 pch1, qint64 pch2, qint64 txch,
const QList<qint64> &channels, qint64 line, qint64 column, QString &errorMessage);
virtual bool handleRoamingZone(qint64 idx, const QString &name, const QList<qint64> &channels,
qint64 line, qint64 column, QString &errorMessage);
protected:
/** If @c true, the reader is in "link" mode. */
bool _link;
/** The configuration to read. */
Config *_config;
/** Index <-> Channel map. */
QMap<int, Channel *> _channels;
/** Index <-> Digital contact map. */
QMap<int, DMRContact *> _digital_contacts;
/** Index <-> RX group list map. */
QMap<int, RXGroupList *> _rxgroups;
/** Index <-> Zone map. */
QMap<int, Zone *> _zones;
/** Index <-> GPS System map. */
QMap<int, PositioningSystem *> _posSystems;
/** Index <-> Scan list map. */
QMap<int, ScanList *> _scanlists;
/** Index <-> RoamingZone map */
QMap<int, RoamingZone *> _roamingZones;
/** Index <-> radio ID map */
QMap<int, DMRRadioID *> _radioIDs;
};
#endif // CSVREADER_HH
|