Commit 74a08fe1 authored by Volker Krause's avatar Volker Krause
Browse files

Add a template-based UIC 918.3 block finding method

This removes the hardcoded block id strings all over the place.
parent 859e6d42
Pipeline #51559 passed with stage
in 10 minutes and 46 seconds
......@@ -7,6 +7,8 @@
#include "uic9183block.h"
#include "logging.h"
#include <cstring>
using namespace KItinerary;
enum {
......@@ -42,6 +44,11 @@ const char* Uic9183Block::name() const
return m_data.constData() + m_offset;
}
bool Uic9183Block::isA(const char recordId[6]) const
{
return std::strncmp(name(), recordId, 6) == 0;
}
const char* Uic9183Block::content() const
{
if (isNull()) {
......
......@@ -33,6 +33,15 @@ public:
* The name is either "U_" + 4 letter standard type or a 4 digit vendor id + 2 char vendor type
*/
const char *name() const;
/** Checks if this block has the given record id. */
bool isA(const char recordId[6]) const;
/** Checks if a block is of type @tparam T. */
template <typename T> inline bool isA() const
{
return isA(T::RecordId);
}
/** Returns the payload data (not including the block header). */
const char *content() const;
/** Returns the size of the entire block data. */
......
......@@ -8,6 +8,7 @@
#include "logging.h"
#include "rct2ticket.h"
#include "uic9183block.h"
#include "uic9183head.h"
#include "uic9183ticketlayout.h"
#include "vendor0080block.h"
......@@ -47,12 +48,10 @@ Uic9183Block Uic9183Parser::firstBlock() const
Uic9183Block Uic9183Parser::findBlock(const char name[6]) const
{
auto block = firstBlock();
while (!block.isNull()) {
if (strncmp(name, block.name(), 6) == 0) {
for (auto block = firstBlock(); !block.isNull(); block = block.nextBlock()) {
if (block.isA(name)) {
return block;
}
block = block.nextBlock();
}
return {};
}
......@@ -129,37 +128,20 @@ bool Uic9183Parser::isValid() const
return !d->m_payload.isEmpty();
}
// U_HEAD Block (version 1, size 53)
// 4x issuing carrier id
// 6x PNR
// 20x unique ticket key
// 12x issuing date/time as ddMMyyyyHHMM, as UTC
// 1x flags
// 2x ticket language
// 2x secondary ticket language
QString Uic9183Parser::pnr() const
{
const auto b = findBlock("U_HEAD");
if (b.isNull() || b.version() != 1 || b.size() != 53) {
return {};
}
return QString::fromUtf8(b.content() + 4, 6);
return findBlock<Uic9183Head>().ticketKey().left(6);
}
QString Uic9183Parser::carrierId() const
{
const auto b = findBlock("U_HEAD");
if (b.isNull() || b.version() != 1 || b.size() != 53) {
return {};
}
return QString::fromUtf8(b.content(), 4);
return findBlock<Uic9183Head>().issuerCompanyCodeString();
}
Person Uic9183Parser::person() const
{
// Deutsche Bahn vendor block
const auto b = Vendor0080BLBlock(findBlock("0080BL"));
const auto b = findBlock<Vendor0080BLBlock>();
if (b.isValid()) {
// S028 contains family and given name separated by a '#', UTF-8 encoded
auto sblock = b.findSubBlock("028");
......@@ -199,7 +181,7 @@ Person Uic9183Parser::person() const
QString Uic9183Parser::outboundDepartureStationId() const
{
const auto b = Vendor0080BLBlock(findBlock("0080BL"));
const auto b = findBlock<Vendor0080BLBlock>();
if (b.isValid()) {
// S035 contains the IBNR, possible with leading '80' country code and leading 0 stripped
const auto sblock = b.findSubBlock("035");
......@@ -214,7 +196,7 @@ QString Uic9183Parser::outboundDepartureStationId() const
QString Uic9183Parser::outboundArrivalStationId() const
{
const auto b = Vendor0080BLBlock(findBlock("0080BL"));
const auto b = findBlock<Vendor0080BLBlock>();
if (b.isValid()) {
// S036 contains the IBNR, possible with leading '80' country code and leading 0 stripped
const auto sblock = b.findSubBlock("036");
......@@ -229,7 +211,7 @@ QString Uic9183Parser::outboundArrivalStationId() const
QString Uic9183Parser::seatingType() const
{
const auto b = Vendor0080BLBlock(findBlock("0080BL"));
const auto b = findBlock<Vendor0080BLBlock>();;
if (b.isValid()) {
// S014 contains the class, possibly with a leading 'S' for some reason
const auto sblock = b.findSubBlock("014");
......@@ -248,9 +230,7 @@ QString Uic9183Parser::seatingType() const
Uic9183TicketLayout Uic9183Parser::ticketLayout() const
{
const auto block = findBlock("U_TLAY");
Uic9183TicketLayout layout(block);
return layout;
return findBlock<Uic9183TicketLayout>();
}
QVariant Uic9183Parser::ticketLayoutVariant() const
......
......@@ -89,6 +89,14 @@ public:
* A null block is returned if no such block exists.
*/
Uic9183Block findBlock(const char name[6]) const;
/** Returns the first block of type @tparam T.
* A null block is returned if no such block exists.
*/
template <typename T>
inline T findBlock() const
{
return T(findBlock(T::RecordId));
}
/** Same as the above, but for JS usage. */
Q_INVOKABLE QVariant block(const QString &name) const;
......
......@@ -92,6 +92,8 @@ public:
* Prefer text() over this to avoid doing your own text layout assembly.
*/
Uic9183TicketLayoutField firstField() const;
static constexpr const char RecordId[] = "U_TLAY";
private:
QExplicitlySharedDataPointer<Uic9183TicketLayoutPrivate> d;
};
......
......@@ -56,6 +56,7 @@ public:
/** Finds a S-block by type. */
Vendor0080BLSubBlock findSubBlock(const char id[3]) const;
static constexpr const char RecordId[] = "0080BL";
private:
static int subblockOffset(const Uic9183Block &block);
......
......@@ -5,6 +5,7 @@
*/
#include "../lib/era/ssbticket.h"
#include "../lib/uic9183/uic9183head.h"
#include <kitinerary_version.h>
......@@ -77,32 +78,51 @@ void dumpUic9183(const QByteArray &data)
parser.parse(data);
// TODO dump header
auto block = parser.firstBlock();
while (!block.isNull()) {
for (auto block = parser.firstBlock(); !block.isNull(); block = block.nextBlock()) {
std::cout << "Block: ";
std::cout.write(block.name(), 6);
std::cout << ", size: " << block.size() << ", version: " << block.version() << std::endl;
if (std::strncmp(block.name(), "U_TLAY", 6) == 0) {
if (block.isA<Uic9183Head>()) {
Uic9183Head head(block);
for (auto i = 0; i < Uic9183Head::staticMetaObject.propertyCount(); ++i) {
const auto prop = Uic9183Head::staticMetaObject.property(i);
const auto value = prop.readOnGadget(&head);
std::cout << " " << prop.name() << ": " << qPrintable(value.toString()) << std::endl;
}
} else if (block.isA<Uic9183TicketLayout>()) {
Uic9183TicketLayout tlay(block);
// TODO
} else if (std::strncmp(block.name(), "0080BL", 6) == 0) {
std::cout << " Layout standard: " << qPrintable(tlay.type()) << std::endl;
for (auto field = tlay.firstField(); !field.isNull(); field = field.next()) {
std::cout << " [row: " << field.row() << " column: " << field.column()
<< " height: " << field.height() << " width: " << field.width()
<< " format: " << field.format() << "]: " << qPrintable(field.text())
<< std::endl;
}
} else if (block.isA<Vendor0080BLBlock>()) {
Vendor0080BLBlock vendor(block);
auto sub = vendor.firstBlock();
while (!sub.isNull()) {
for (auto sub = vendor.firstBlock(); !sub.isNull(); sub = sub.nextBlock()) {
std::cout << " ";
std::cout.write(sub.id(), 3);
std::cout << " (size: " << sub.size() << "): ";
dumpRawData(sub.content(), sub.contentSize());
std::cout << std::endl;
sub = sub.nextBlock();
}
} else {
std::cout << " Content: ";
dumpRawData(block.content(), block.contentSize());
std::cout << std::endl;
}
block = block.nextBlock();
}
}
void dumpVdv(const QByteArray &data)
{
VdvTicketParser parser;
if (!parser.parse(data)) {
std::cerr << "failed to parse VDV ticket" << std::endl;
return;
}
}
......@@ -145,7 +165,7 @@ int main(int argc, char **argv)
dumpUic9183(data);
} else if (VdvTicketParser::maybeVdvTicket(data)) {
std::cout << "VDV Ticket" << std::endl;
// TODO
dumpVdv(data);
} else {
std::cout << "Unknown content" << std::endl;
return 1;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment