added decode/encode commands, use [DD]* instead of Dx in help
This commit is contained in:
@@ -44,6 +44,11 @@ static const char* defaultTemplateFieldMap[] = {
|
||||
"*name", "type", "divisor/values", "unit", "comment",
|
||||
};
|
||||
|
||||
/** the default field map for fields only. */
|
||||
static const char* defaultFieldsFieldMap[] = {
|
||||
"*type", "divisor/values", "unit", "comment",
|
||||
};
|
||||
|
||||
|
||||
string AttributedItem::formatInt(size_t value) {
|
||||
ostringstream stream;
|
||||
@@ -1129,6 +1134,124 @@ result_t DataFieldSet::write(char separator, size_t offset, istringstream* input
|
||||
}
|
||||
|
||||
|
||||
result_t LoadableDataFieldSet::getFieldMap(const string& preferLanguage, vector<string>* row,
|
||||
string* errorDescription) const {
|
||||
// *type,divisor/values,unit,comment
|
||||
if (row->empty()) {
|
||||
for (const auto& col : defaultFieldsFieldMap) {
|
||||
row->push_back(col);
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
map<string, size_t> seen;
|
||||
for (size_t col = 0; col < row->size(); col++) {
|
||||
string &name = (*row)[col];
|
||||
string lowerName = name;
|
||||
tolower(&lowerName);
|
||||
trim(&lowerName);
|
||||
bool toDataFields;
|
||||
if (!lowerName.empty() && lowerName[0] == '*') {
|
||||
lowerName.erase(0, 1);
|
||||
toDataFields = true;
|
||||
} else {
|
||||
toDataFields = col == 0;
|
||||
}
|
||||
if (lowerName.empty()) {
|
||||
*errorDescription = "missing name in column " + AttributedItem::formatInt(col);
|
||||
return RESULT_ERR_INVALID_ARG;
|
||||
}
|
||||
if (toDataFields) {
|
||||
if (!seen.empty() && seen.find("type") == seen.end()) {
|
||||
*errorDescription = "missing field type";
|
||||
return RESULT_ERR_EOF; // require at least name and type
|
||||
}
|
||||
seen.clear();
|
||||
}
|
||||
size_t langPos = lowerName.find_last_of('.');
|
||||
if (langPos != string::npos && langPos > 0 && langPos == lowerName.length()-3) {
|
||||
string lang = lowerName.substr(langPos+1);
|
||||
lowerName.erase(langPos);
|
||||
map<string, size_t>::iterator previous = seen.find(lowerName);
|
||||
if (previous != seen.end()) {
|
||||
if (lang != preferLanguage) {
|
||||
// skip this column
|
||||
name = SKIP_COLUMN;
|
||||
continue;
|
||||
}
|
||||
// replace previous
|
||||
(*row)[previous->second] = SKIP_COLUMN;
|
||||
seen.erase(lowerName);
|
||||
}
|
||||
} else {
|
||||
map<string, size_t>::iterator previous = seen.find(lowerName);
|
||||
if (previous != seen.end()) {
|
||||
*errorDescription = "duplicate field " + name;
|
||||
return RESULT_ERR_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
name = toDataFields ? "*"+lowerName : lowerName;
|
||||
seen[lowerName] = col;
|
||||
}
|
||||
if (seen.find("type") == seen.end()) {
|
||||
*errorDescription = "missing field type";
|
||||
return RESULT_ERR_EOF; // require at least type
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
result_t LoadableDataFieldSet::addFromFile(const string& filename, unsigned int lineNo, map<string, string>* row,
|
||||
vector< map<string, string> >* subRows, string* errorDescription, bool replace) {
|
||||
const DataField* field = NULL;
|
||||
result_t result = DataField::create(false, false, false, MAX_POS, m_templates, subRows, errorDescription, &field);
|
||||
if (result != RESULT_OK) {
|
||||
return result;
|
||||
}
|
||||
if (!field) {
|
||||
return RESULT_ERR_INVALID_ARG;
|
||||
}
|
||||
map<string, string> names;
|
||||
for (auto check : m_fields) {
|
||||
if (check->isIgnored()) {
|
||||
continue;
|
||||
}
|
||||
string name = check->getName(-1);
|
||||
if (!name.empty()) {
|
||||
names[name] = name;
|
||||
}
|
||||
}
|
||||
if (field->isSet()) {
|
||||
const DataFieldSet* fieldSet = dynamic_cast<const DataFieldSet*>(field);
|
||||
for (auto sfield : fieldSet->m_fields) {
|
||||
m_fields.push_back(sfield);
|
||||
if (sfield->isIgnored()) {
|
||||
m_ignoredCount++;
|
||||
continue;
|
||||
}
|
||||
string name = sfield->getName(-1);
|
||||
if (name.empty() || names.find(name) != names.end()) {
|
||||
m_uniqueNames = false;
|
||||
} else {
|
||||
names[name] = name;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const SingleDataField* sfield = dynamic_cast<const SingleDataField*>(field);
|
||||
m_fields.push_back(sfield);
|
||||
if (sfield->isIgnored()) {
|
||||
m_ignoredCount++;
|
||||
} else {
|
||||
string name = sfield->getName(-1);
|
||||
if (name.empty() || names.find(name) != names.end()) {
|
||||
m_uniqueNames = false;
|
||||
} else {
|
||||
names[name] = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DataFieldTemplates::DataFieldTemplates(const DataFieldTemplates& other)
|
||||
: MappedFileReader::MappedFileReader(false) {
|
||||
for (const auto it : other.m_fieldsByName) {
|
||||
|
||||
+38
-1
@@ -203,6 +203,12 @@ class DataField : public AttributedItem {
|
||||
*/
|
||||
virtual const DataField* clone() const = 0;
|
||||
|
||||
/**
|
||||
* Return whether this is a @a DataFieldSet.
|
||||
* @return true if this is a @a DataFieldSet.
|
||||
*/
|
||||
virtual bool isSet() const { return false; };
|
||||
|
||||
/**
|
||||
* Factory method for creating new instances.
|
||||
* @param isWriteMessage whether the field is part of a write message (default false).
|
||||
@@ -589,6 +595,7 @@ class ConstantDataField : public SingleDataField {
|
||||
* A set of @a DataField instances.
|
||||
*/
|
||||
class DataFieldSet : public DataField {
|
||||
friend class LoadableDataFieldSet;
|
||||
public:
|
||||
/**
|
||||
* Get the @a DataFieldSet for parsing the identification message (service 0x07 0x04).
|
||||
@@ -636,6 +643,9 @@ class DataFieldSet : public DataField {
|
||||
// @copydoc
|
||||
const DataFieldSet* clone() const override;
|
||||
|
||||
// @copydoc
|
||||
bool isSet() const override { return true; };
|
||||
|
||||
// @copydoc
|
||||
size_t getLength(PartType partType, size_t maxLength) const override;
|
||||
|
||||
@@ -692,8 +702,9 @@ class DataFieldSet : public DataField {
|
||||
/** the @a DataFieldSet containing the ident message @a SingleDataField instances, or NULL. */
|
||||
static DataFieldSet* s_identFields;
|
||||
|
||||
protected:
|
||||
/** the @a vector of @a SingleDataField instances part of this set. */
|
||||
const vector<const SingleDataField*> m_fields;
|
||||
vector<const SingleDataField*> m_fields;
|
||||
|
||||
/** whether all fields have a unique name. */
|
||||
bool m_uniqueNames;
|
||||
@@ -703,6 +714,32 @@ class DataFieldSet : public DataField {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A special @a DataFieldSet that supports loading via @a MappedFileReader.
|
||||
*/
|
||||
class LoadableDataFieldSet : public DataFieldSet, public MappedFileReader {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
* @param name the field name.
|
||||
* @param fields the @a vector of @a SingleDataField instances part of this set.
|
||||
*/
|
||||
LoadableDataFieldSet(const string& name, DataFieldTemplates* templates)
|
||||
: DataFieldSet(name, vector<const SingleDataField*>()), MappedFileReader(false), m_templates(templates) {
|
||||
}
|
||||
|
||||
// @copydoc
|
||||
result_t getFieldMap(const string& preferLanguage, vector<string>* row, string* errorDescription) const override;
|
||||
|
||||
// @copydoc
|
||||
result_t addFromFile(const string& filename, unsigned int lineNo, map<string, string>* row,
|
||||
vector< map<string, string> >* subRows, string* errorDescription, bool replace) override;
|
||||
|
||||
private:
|
||||
DataFieldTemplates* m_templates;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A map of template @a DataField instances.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user