Skip to content

Commit

Permalink
Stronger dimension name checking.
Browse files Browse the repository at this point in the history
  • Loading branch information
abellgithub committed Jan 14, 2020
1 parent 97df6f0 commit 4fb6816
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 5 deletions.
48 changes: 47 additions & 1 deletion io/TextReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,12 @@ QuickInfo TextReader::inspect()
}


// Make sure we have a header line.
void TextReader::checkHeader(const std::string& header)
{
auto it = std::find_if(header.begin(), header.end(),
[](char c){ return std::isalpha(c); });

if (it == header.end())
log()->get(LogLevel::Warning) << getName() <<
": file '" << m_filename <<
Expand All @@ -100,6 +102,43 @@ void TextReader::checkHeader(const std::string& header)


void TextReader::parseHeader(const std::string& header)
{
// If the first character is a double quote, assume that we have quoted
// field names.
if (header[0] == '"')
parseQuotedHeader(header);
else
parseUnquotedHeader(header);
}


void TextReader::parseQuotedHeader(const std::string& header)
{
if (m_separatorArg->set())
throwError("Separator option not supported with a header line "
"containing quoted dimension names.");

// We know there's a double quote at position 0.
std::string::size_type pos = 1;
while (true)
{
size_t count = Dimension::extractName(header, pos);
m_dimNames.push_back(header.substr(pos, count));
pos += count;
if (header[pos] != '"')
throwError("Invalid character '" + std::string(1, header[pos]) +
"' found while parsing quoted header line.");
pos++; // Skip ending quote.

// Skip everything other than a double quote.
count = Utils::extract(header, pos, [](char c){ return c != '"'; });
pos += count;
if (header[pos++] != '"')
break;
}
}

void TextReader::parseUnquotedHeader(const std::string& header)
{
auto isspecial = [](char c)
{ return (!std::isalnum(c)); };
Expand All @@ -108,7 +147,7 @@ void TextReader::parseHeader(const std::string& header)
// from the header line.
if (!m_separatorArg->set())
{
// Scan string for some character not a number, space or letter.
// Scan string for some character not a number or letter.
for (size_t i = 0; i < header.size(); ++i)
if (isspecial(header[i]))
{
Expand All @@ -121,6 +160,13 @@ void TextReader::parseHeader(const std::string& header)
m_dimNames = Utils::split(header, m_separator);
else
m_dimNames = Utils::split2(header, m_separator);
for (auto& s : m_dimNames)
{
size_t cnt = Dimension::extractName(s, 0);
if (cnt != s.size())
throwError("Invalid character '" + std::string(1, s[cnt]) +
"' in dimension name.");
}
}


Expand Down
15 changes: 14 additions & 1 deletion io/TextReader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,23 @@ class PDAL_DLL TextReader : public Reader, public Streamable
Parse a header line into a list of dimension names.
\param header Header line to parse.
\return List of dimension names.
*/
void parseHeader(const std::string& header);

/**
Parse a header line that starts with a quote.
\param header Header line to parse.
*/
void parseQuotedHeader(const std::string& header);

/**
Parse a header line that doesn't start with a quote.
\param header Header line to parse.
*/
void parseUnquotedHeader(const std::string& header);

/**
Check a header line to see if it appears header-like. Display a
warning if it doesn't look like a header.
Expand Down
52 changes: 49 additions & 3 deletions test/unit/io/TextReaderTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,13 +278,59 @@ TEST(TextReaderTest, insertHeader)

PointTable table;
reader.prepare(table);
PointViewSet pointViewSet = reader.execute(table);
PointViewPtr pointViewPtr = *pointViewSet.begin();
PointViewSet s = reader.execute(table);
PointViewPtr v = *s.begin();

EXPECT_EQ(pointViewPtr->size(), 11U);
EXPECT_EQ(v->size(), 11U);
PointLayoutPtr layout = table.layout();
EXPECT_TRUE(layout->findDim("A") != Dimension::Id::Unknown);
EXPECT_TRUE(layout->findDim("B") != Dimension::Id::Unknown);
EXPECT_TRUE(layout->findDim("C") != Dimension::Id::Unknown);
EXPECT_TRUE(layout->findDim("G") != Dimension::Id::Unknown);
}

TEST(TextReaderTest, quotedHeader)
{
auto testme = [](Options& options)
{
TextReader reader;
options.add("filename", Support::datapath("text/quoted.txt"));
reader.setOptions(options);

PointTable table;
reader.prepare(table);
PointViewSet s = reader.execute(table);
PointViewPtr v = *s.begin();
EXPECT_EQ(v->size(), 9U);
PointLayoutPtr layout = table.layout();
EXPECT_TRUE(layout->findDim("X") != Dimension::Id::Unknown);
EXPECT_TRUE(layout->findDim("Y") != Dimension::Id::Unknown);
EXPECT_TRUE(layout->findDim("Z") != Dimension::Id::Unknown);
};

{
Options opts;
testme(opts);
}

{
Options opts;
opts.add("header", "\"X\"\"Y\" \"Z\" ");
opts.add("skip", 1);
testme(opts);
}

{
Options opts;
opts.add("header", "\"X\"\"Y\" \" ");
opts.add("skip", 1);
EXPECT_THROW(testme(opts), pdal_error);
}

{
Options opts;
opts.add("header", " \"X\"\"Y\" \" ");
opts.add("skip", 1);
EXPECT_THROW(testme(opts), pdal_error);
}
}

0 comments on commit 4fb6816

Please sign in to comment.