mirror of
https://github.com/PDB-REDO/libcifpp.git
synced 2026-06-04 13:54:25 +08:00
format output
This commit is contained in:
@@ -26,9 +26,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cif++/forward_decl.hpp"
|
||||
|
||||
#include "cif++/condition.hpp"
|
||||
#include "cif++/forward_decl.hpp"
|
||||
#include "cif++/iterator.hpp"
|
||||
#include "cif++/row.hpp"
|
||||
#include "cif++/text.hpp"
|
||||
@@ -1133,16 +1132,46 @@ class category
|
||||
/// Write the contents of the category to the std::ostream @a os
|
||||
void write(std::ostream &os) const;
|
||||
|
||||
/// \brief Various supported output formats
|
||||
enum class output_format
|
||||
{
|
||||
cif, // Output in mmCIF format
|
||||
csv, // comma separated values
|
||||
tsv, // tab separated values
|
||||
list, // values delimited by a '|' character
|
||||
column, // output in columns
|
||||
markdown, //
|
||||
table, // ascii art table
|
||||
};
|
||||
|
||||
/// @brief
|
||||
/// @brief Write the contents of the category to the std::ostream @a os and
|
||||
/// use @a order as the order of the items. If @a addMissingItems is
|
||||
/// false, items that do not contain any value will be suppressed. Use this version
|
||||
/// to write out
|
||||
/// @param os The std::ostream to write to
|
||||
/// @param fmt The format to use
|
||||
/// @param order The order in which the items should appear
|
||||
/// @param addMissingItems When false, empty items are suppressed from the output
|
||||
void write(std::ostream &os, output_format fmt,
|
||||
const std::vector<std::string> &order, bool addMissingItems = true);
|
||||
|
||||
/// @brief Write the contents of the category to the std::ostream @a os and
|
||||
/// use @a order as the order of the items. If @a addMissingItems is
|
||||
/// false, items that do not contain any value will be suppressed
|
||||
/// @param os The std::ostream to write to
|
||||
/// @param order The order in which the items should appear
|
||||
/// @param addMissingItems When false, empty items are suppressed from the output
|
||||
void write(std::ostream &os, const std::vector<std::string> &order, bool addMissingItems = true);
|
||||
void write(std::ostream &os, const std::vector<std::string> &order, bool addMissingItems = true)
|
||||
{
|
||||
write(os, output_format::cif, order, addMissingItems);
|
||||
}
|
||||
|
||||
private:
|
||||
void write(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyItems) const;
|
||||
void write_cif(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyItems) const;
|
||||
void write_delimited(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyItems, std::string_view delimiter, bool aligned) const;
|
||||
void write_markdown(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyItems) const;
|
||||
void write_table(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyItems) const;
|
||||
|
||||
public:
|
||||
/// friend function to make it possible to do:
|
||||
|
||||
187
src/category.cpp
187
src/category.cpp
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
|
||||
#include "cif++/category.hpp"
|
||||
|
||||
#include "cif++/datablock.hpp"
|
||||
#include "cif++/parser.hpp"
|
||||
#include "cif++/utilities.hpp"
|
||||
@@ -32,6 +33,7 @@
|
||||
|
||||
#include <numeric>
|
||||
#include <stack>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
// TODO: Find out what the rules are exactly for linked items, the current implementation
|
||||
@@ -109,7 +111,7 @@ class row_comparator
|
||||
std::string_view ka = ai->value;
|
||||
std::string_view kb = rhb[k].text();
|
||||
|
||||
if (not (ai->may_be_null and rhb[k].empty()))
|
||||
if (not(ai->may_be_null and rhb[k].empty()))
|
||||
d = f(ka, kb);
|
||||
|
||||
if (d != 0)
|
||||
@@ -1913,10 +1915,10 @@ void category::write(std::ostream &os) const
|
||||
{
|
||||
std::vector<uint16_t> order(m_items.size());
|
||||
iota(order.begin(), order.end(), static_cast<uint16_t>(0));
|
||||
write(os, order, false);
|
||||
write_cif(os, order, false);
|
||||
}
|
||||
|
||||
void category::write(std::ostream &os, const std::vector<std::string> &items, bool addMissingItems)
|
||||
void category::write(std::ostream &os, output_format fmt, const std::vector<std::string> &items, bool addMissingItems)
|
||||
{
|
||||
// make sure all items are present
|
||||
for (auto &c : items)
|
||||
@@ -1937,10 +1939,39 @@ void category::write(std::ostream &os, const std::vector<std::string> &items, bo
|
||||
}
|
||||
}
|
||||
|
||||
write(os, order, true);
|
||||
switch (fmt)
|
||||
{
|
||||
case output_format::cif:
|
||||
write_cif(os, order, addMissingItems);
|
||||
break;
|
||||
|
||||
case output_format::csv:
|
||||
write_delimited(os, order, addMissingItems, ",", false);
|
||||
break;
|
||||
|
||||
case output_format::tsv:
|
||||
write_delimited(os, order, addMissingItems, "\t", false);
|
||||
break;
|
||||
|
||||
case output_format::list:
|
||||
write_delimited(os, order, addMissingItems, "|", false);
|
||||
break;
|
||||
|
||||
case output_format::column:
|
||||
write_delimited(os, order, addMissingItems, " ", true);
|
||||
break;
|
||||
|
||||
case output_format::markdown:
|
||||
write_markdown(os, order, addMissingItems);
|
||||
break;
|
||||
|
||||
case output_format::table:
|
||||
write_table(os, order, addMissingItems);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void category::write(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyItems) const
|
||||
void category::write_cif(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyItems) const
|
||||
{
|
||||
if (empty())
|
||||
return;
|
||||
@@ -2113,6 +2144,152 @@ void category::write(std::ostream &os, const std::vector<uint16_t> &order, bool
|
||||
os << "# \n";
|
||||
}
|
||||
|
||||
void category::write_delimited(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyItems, std::string_view delimiter, bool aligned) const
|
||||
{
|
||||
if (empty())
|
||||
return;
|
||||
|
||||
std::vector<bool> right_aligned(m_items.size(), false);
|
||||
|
||||
if (m_cat_validator != nullptr)
|
||||
{
|
||||
for (auto cix : order)
|
||||
{
|
||||
auto &col = m_items[cix];
|
||||
right_aligned[cix] = col.m_validator != nullptr and
|
||||
col.m_validator->m_type != nullptr and
|
||||
col.m_validator->m_type->m_primitive_type == cif::DDL_PrimitiveType::Numb;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::size_t> itemWidths(m_items.size());
|
||||
auto get_line = [delimiter](std::string_view s) -> std::string
|
||||
{
|
||||
if (delimiter == ",")
|
||||
{
|
||||
if (s.find_first_of("\",") == std::string::npos)
|
||||
return std::string{ s };
|
||||
|
||||
std::string r{ '"' };
|
||||
r.reserve(s.length() + 2);
|
||||
for (auto ch : s)
|
||||
{
|
||||
if (ch == '"')
|
||||
r.append("\"\"");
|
||||
else
|
||||
r.push_back(ch);
|
||||
}
|
||||
r.push_back('"');
|
||||
return r;
|
||||
}
|
||||
else if (delimiter == "\t")
|
||||
{
|
||||
std::string r;
|
||||
r.reserve(s.length());
|
||||
for (auto ch : s)
|
||||
{
|
||||
if (ch == '\r' or ch == '\n' or ch == '\t' or ch == '\\')
|
||||
r.push_back('\\');
|
||||
r.push_back(ch);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
else if (delimiter == "|" or delimiter == " ")
|
||||
return std::string{ s };
|
||||
else
|
||||
assert(false);
|
||||
};
|
||||
|
||||
if (aligned)
|
||||
{
|
||||
for (auto cix : order)
|
||||
{
|
||||
auto &col = m_items[cix];
|
||||
itemWidths[cix] = col.m_name.length();
|
||||
}
|
||||
|
||||
for (auto r = m_head; r != nullptr; r = r->m_next)
|
||||
{
|
||||
for (uint16_t ix = 0; ix < r->size(); ++ix)
|
||||
{
|
||||
auto v = r->get(ix);
|
||||
if (v == nullptr)
|
||||
continue;
|
||||
|
||||
size_t l = get_line(v->text()).length();
|
||||
if (itemWidths[ix] < l)
|
||||
itemWidths[ix] = l;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (true /* header */)
|
||||
{
|
||||
for (bool first = true; uint16_t cix : order)
|
||||
{
|
||||
if (not std::exchange(first, false))
|
||||
os << delimiter;
|
||||
|
||||
std::size_t w = itemWidths[cix];
|
||||
std::string_view s = m_items[cix].m_name;
|
||||
|
||||
if (s.length() < w)
|
||||
{
|
||||
if (right_aligned[cix])
|
||||
os << std::string(w - s.length(), ' ');
|
||||
os << s;
|
||||
if (not right_aligned[cix])
|
||||
os << std::string(w - s.length(), ' ');
|
||||
}
|
||||
else
|
||||
os << s;
|
||||
}
|
||||
|
||||
os << '\n';
|
||||
}
|
||||
|
||||
for (auto r = m_head; r != nullptr; r = r->m_next) // loop over rows
|
||||
{
|
||||
for (bool first = true; uint16_t cix : order)
|
||||
{
|
||||
if (not std::exchange(first, false))
|
||||
os << delimiter;
|
||||
|
||||
std::size_t w = itemWidths[cix];
|
||||
|
||||
std::string_view s;
|
||||
auto iv = r->get(cix);
|
||||
|
||||
if (iv != nullptr)
|
||||
s = iv->text();
|
||||
|
||||
if (s == "?" or s == ".")
|
||||
s = "";
|
||||
|
||||
if (s.length() < w)
|
||||
{
|
||||
if (right_aligned[cix])
|
||||
os << std::string(w - s.length(), ' ');
|
||||
os << s;
|
||||
if (not right_aligned[cix])
|
||||
os << std::string(w - s.length(), ' ');
|
||||
}
|
||||
else
|
||||
os << s;
|
||||
}
|
||||
|
||||
os << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void category::write_markdown(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyItems) const
|
||||
{
|
||||
}
|
||||
|
||||
void category::write_table(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyItems) const
|
||||
{
|
||||
}
|
||||
|
||||
bool category::operator==(const category &rhs) const
|
||||
{
|
||||
// shortcut
|
||||
|
||||
Reference in New Issue
Block a user