Commit a855f880 by Maarten L. Hekkelman

Getting rid of boost/algorithm/string

parent cfa2acd6
......@@ -6,10 +6,10 @@
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer
# list of conditions and the following disclaimer
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
......@@ -46,7 +46,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers")
elseif(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
# Building shared libraries?
......@@ -57,6 +57,7 @@ set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
# Optionally build a version to be installed inside CCP4
option(BUILD_FOR_CCP4 "Build a version to be installed in CCP4" OFF)
if(BUILD_FOR_CCP4)
if("$ENV{CCP4}" STREQUAL "" OR NOT EXISTS $ENV{CCP4})
message(FATAL_ERROR "A CCP4 built was requested but CCP4 was not sourced")
......@@ -79,6 +80,7 @@ if(EXISTS "$ENV{CCP4}")
set(CCP4 $ENV{CCP4})
set(CLIBD ${CCP4}/lib/data)
endif()
if(CCP4 AND NOT CLIBD)
set(CLIBD ${CCP4}/lib/data)
endif()
......@@ -97,13 +99,12 @@ else()
endif()
# set(CMAKE_DEBUG_POSTFIX d)
if(MSVC)
# make msvc standards compliant...
add_compile_options(/permissive-)
# make msvc standards compliant...
add_compile_options(/permissive-)
macro(get_WIN32_WINNT version)
if (WIN32 AND CMAKE_SYSTEM_VERSION)
if(WIN32 AND CMAKE_SYSTEM_VERSION)
set(ver ${CMAKE_SYSTEM_VERSION})
string(REPLACE "." "" ver ${ver})
string(REGEX REPLACE "([0-9])" "0\\1" ver ${ver})
......@@ -129,7 +130,6 @@ if(UNIX AND NOT APPLE AND NOT BUILD_FOR_CCP4 AND CMAKE_INSTALL_PREFIX_INITIALIZE
endif()
# Optionally use mrc to create resources
if(WIN32 AND BUILD_SHARED_LIBS)
message("Not using resources when building shared libraries for Windows")
else()
......@@ -150,17 +150,12 @@ else()
endif()
# Libraries
set(CMAKE_THREAD_PREFER_PTHREAD)
set(THREADS_PREFER_PTHREAD_FLAG)
find_package(Threads)
find_package(Boost 1.70.0 REQUIRED COMPONENTS system iostreams regex program_options)
if(NOT MSVC AND Boost_USE_STATIC_LIBS)
find_package(ZLIB REQUIRED)
list(APPEND CIFPP_REQUIRED_LIBRARIES ZLIB::ZLIB)
endif()
find_package(Boost 1.70.0 REQUIRED COMPONENTS system regex program_options)
find_package(gzstream REQUIRED)
include(FindFilesystem)
list(APPEND CIFPP_REQUIRED_LIBRARIES ${STDCPPFS_LIBRARY})
......@@ -175,21 +170,21 @@ write_version_header("LibCIFPP")
# SymOp data table
if(CIFPP_RECREATE_SYMOP_DATA)
# The tool to create the table
add_executable(symop-map-generator "${CMAKE_SOURCE_DIR}/tools/symop-map-generator.cpp")
target_link_libraries(symop-map-generator Threads::Threads ${Boost_LIBRARIES} ${CIFPP_REQUIRED_LIBRARIES})
if(Boost_INCLUDE_DIR)
target_include_directories(symop-map-generator PUBLIC ${Boost_INCLUDE_DIR})
endif()
set($ENV{CLIBD} ${CLIBD})
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/src/SymOpTable_data.hpp
COMMAND $<TARGET_FILE:symop-map-generator> ${CLIBD}/syminfo.lib ${CMAKE_SOURCE_DIR}/src/SymOpTable_data.hpp
)
)
add_custom_target(
OUTPUT ${CMAKE_SOURCE_DIR}/src/SymOpTable_data.hpp
DEPENDS symop-map-generator "$ENV{CLIBD}/syminfo.lib"
......@@ -197,8 +192,7 @@ if(CIFPP_RECREATE_SYMOP_DATA)
endif()
# Sources
set(project_sources
set(project_sources
${PROJECT_SOURCE_DIR}/src/AtomType.cpp
${PROJECT_SOURCE_DIR}/src/BondMap.cpp
${PROJECT_SOURCE_DIR}/src/Cif++.cpp
......@@ -224,7 +218,7 @@ set(project_sources
${PROJECT_SOURCE_DIR}/src/v2/validate.cpp
)
set(project_headers
set(project_headers
${PROJECT_SOURCE_DIR}/include/cif++/AtomType.hpp
${PROJECT_SOURCE_DIR}/include/cif++/BondMap.hpp
${PROJECT_SOURCE_DIR}/include/cif++/Cif++.hpp
......@@ -249,7 +243,7 @@ target_include_directories(cifpp
PUBLIC
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
${Boost_INCLUDE_DIR}
${Boost_INCLUDE_DIR} ${gzstream_INCLUDE_DIR}
)
target_include_directories(cifpp
......@@ -257,21 +251,21 @@ target_include_directories(cifpp
${CMAKE_BINARY_DIR}
)
target_link_libraries(cifpp PUBLIC Threads::Threads Boost::regex Boost::iostreams ${CIFPP_REQUIRED_LIBRARIES})
# target_link_libraries(cifpp PRIVATE)
target_link_libraries(cifpp PUBLIC Threads::Threads Boost::regex gzstream::gzstream ${CIFPP_REQUIRED_LIBRARIES})
if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
target_link_options(cifpp PRIVATE -undefined dynamic_lookup)
endif (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
# target_link_libraries(cifpp PRIVATE)
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
target_link_options(cifpp PRIVATE -undefined dynamic_lookup)
endif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
option(CIFPP_DOWNLOAD_CCD "Download the CCD file components.cif during installation" OFF)
if(CIFPP_DOWNLOAD_CCD)
# download the components.cif file from CCD
set(COMPONENTS_CIF ${PROJECT_SOURCE_DIR}/data/components.cif)
if (NOT EXISTS ${COMPONENTS_CIF})
if (NOT EXISTS ${PROJECT_SOURCE_DIR}/data)
if(NOT EXISTS ${COMPONENTS_CIF})
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/data)
file(MAKE_DIRECTORY ${PROJECT_SOURCE_DIR}/data/)
endif()
......@@ -301,8 +295,8 @@ endif()
generate_export_header(cifpp
EXPORT_FILE_NAME cif++/Cif++Export.hpp)
set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR} )
set(LIBRARY_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR} )
set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR})
set(LIBRARY_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR})
set(SHARE_INSTALL_DIR ${CMAKE_INSTALL_DATADIR}/libcifpp)
set(CIFPP_DATA_DIR "${CMAKE_INSTALL_PREFIX}/${SHARE_INSTALL_DIR}" CACHE STRING "The directory containing the provided data files")
......@@ -310,7 +304,6 @@ set(CIFPP_DATA_DIR "${CMAKE_INSTALL_PREFIX}/${SHARE_INSTALL_DIR}" CACHE STRING "
target_compile_definitions(cifpp PUBLIC DATA_DIR="${CIFPP_DATA_DIR}")
# Install rules
install(TARGETS cifpp
EXPORT cifppTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
......@@ -357,8 +350,8 @@ configure_package_config_file(Config.cmake.in
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifppConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifppConfigVersion.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifppConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifppConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cifpp
COMPONENT Devel
)
......@@ -370,21 +363,20 @@ set_target_properties(cifpp PROPERTIES
INTERFACE_cifpp_MAJOR_VERSION ${cifpp_MAJOR_VERSION})
set_property(TARGET cifpp APPEND PROPERTY
COMPATIBLE_INTERFACE_STRING cifpp_MAJOR_VERSION
COMPATIBLE_INTERFACE_STRING cifpp_MAJOR_VERSION
)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifppConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifppConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
)
# pkgconfig support
set(prefix ${CMAKE_INSTALL_PREFIX})
set(prefix ${CMAKE_INSTALL_PREFIX})
set(exec_prefix ${CMAKE_INSTALL_PREFIX})
set(libdir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
set(includedir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR})
set(libdir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
set(includedir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libcifpp.pc.in
${CMAKE_CURRENT_BINARY_DIR}/libcifpp.pc.in @ONLY)
......@@ -393,12 +385,11 @@ file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.pc
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
# Unit tests
option(CIFPP_BUILD_TESTS "Build test exectuables" OFF)
if(CIFPP_BUILD_TESTS)
list(APPEND CIFPP_tests
# pdb2cif
rename-compound
structure
......@@ -414,10 +405,10 @@ if(CIFPP_BUILD_TESTS)
target_include_directories(${CIFPP_TEST} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_BINARY_DIR} # for config.h
${CMAKE_CURRENT_BINARY_DIR} # for config.h
)
target_link_libraries(${CIFPP_TEST} PRIVATE Threads::Threads cifpp )
target_link_libraries(${CIFPP_TEST} PRIVATE Threads::Threads cifpp)
if(CIFPP_USE_RSRC)
mrc_target_resources(${CIFPP_TEST} ${CMAKE_SOURCE_DIR}/rsrc/mmcif_pdbx_v50.dic)
......@@ -436,14 +427,12 @@ if(CIFPP_BUILD_TESTS)
add_test(NAME ${CIFPP_TEST}
COMMAND $<TARGET_FILE:${CIFPP_TEST}> -- ${CMAKE_SOURCE_DIR}/test)
endforeach()
endif()
message("Will install in ${CMAKE_INSTALL_PREFIX}")
# Optionally install the update scripts for CCD and dictionary files
if(CIFPP_INSTALL_UPDATE_SCRIPT)
set(CIFPP_CRON_DIR "$ENV{DESTDIR}/etc/cron.weekly")
......@@ -468,4 +457,3 @@ if(CIFPP_INSTALL_UPDATE_SCRIPT)
target_compile_definitions(cifpp PUBLIC CACHE_DIR="${CIFPP_CACHE_DIR}")
endif()
......@@ -2,10 +2,8 @@
include(CMakeFindDependencyMacro)
find_dependency(Threads)
find_dependency(Boost 1.70.0 REQUIRED COMPONENTS system iostreams regex)
if(NOT WIN32)
find_dependency(ZLIB)
endif()
find_dependency(Boost 1.70.0 REQUIRED COMPONENTS system regex)
find_dependency(gzstream)
INCLUDE("${CMAKE_CURRENT_LIST_DIR}/cifppTargets.cmake")
......
......@@ -72,7 +72,124 @@ bool iequals(const char *a, const char *b);
int icompare(const char *a, const char *b);
void toLower(std::string &s);
std::string toLowerCopy(const std::string &s);
std::string toLowerCopy(std::string_view s);
void toUpper(std::string &s);
// std::string toUpperCopy(const std::string &s);
template <typename IterType>
std::string join(IterType b, IterType e, std::string_view sep)
{
std::ostringstream s;
if (b != e)
{
auto ai = b;
auto ni = std::next(ai);
for (;;)
{
s << *ai;
ai = ni;
ni = std::next(ai);
if (ni == e)
break;
s << sep;
}
}
return s.str();
}
template <typename V>
std::string join(const V &arr, std::string_view sep)
{
return join(arr.begin(), arr.end(), sep);
}
template<typename StringType = std::string_view>
std::vector<StringType> split(std::string_view s, std::string_view separators, bool suppress_empty = false)
{
std::vector<StringType> result;
auto b = s.begin();
auto e = b;
while (e != s.end())
{
if (separators.find(*e) != std::string_view::npos)
{
if (e > b or not suppress_empty)
result.emplace_back(b, e - b);
b = e = e + 1;
continue;
}
++e;
}
if (e > b or not suppress_empty)
result.emplace_back(b, e - b);
return result;
}
void replace_all(std::string &s, std::string_view what, std::string_view with = {});
#if defined(__cpp_lib_starts_ends_with)
inline bool starts_with(std::string s, std::string_view with)
{
return s.starts_with(with);
}
inline bool ends_with(std::string_view s, std::string_view with)
{
return s.ends_with(with);
}
#else
inline bool starts_with(std::string s, std::string_view with)
{
return s.compare(0, with.length(), with) == 0;
}
inline bool ends_with(std::string_view s, std::string_view with)
{
return s.length() >= with.length() and s.compare(s.length() - with.length(), with.length(), with) == 0;
}
#endif
#if defined(__cpp_lib_string_contains)
inline bool contains(std::string_view s, std::string_view q)
{
return s.contains(q);
}
#else
inline bool contains(std::string_view s, std::string_view q)
{
return s.find(q) != std::string_view::npos;
}
#endif
bool icontains(std::string_view s, std::string_view q);
void trim_left(std::string &s);
void trim_right(std::string &s);
void trim(std::string &s);
std::string trim_left_copy(std::string_view s);
std::string trim_right_copy(std::string_view s);
std::string trim_copy(std::string_view s);
// To make life easier, we also define iless and iset using iequals
......
......@@ -28,6 +28,8 @@
#pragma once
#include <vector>
namespace mmcif
{
......
......@@ -599,8 +599,6 @@ class File : public cif::File
void load(const std::filesystem::path &p) override;
void save(const std::filesystem::path &p) override;
void load(std::istream &is) override;
using cif::File::load;
using cif::File::save;
......
......@@ -394,6 +394,34 @@ class category
}
// --------------------------------------------------------------------
/// \brief generate a new, unique ID. Pass it an ID generating function
/// based on a sequence number. This function will be called until the
/// result is unique in the context of this category
std::string get_unique_id(std::function<std::string(int)> generator = cif::cifIdForNumber);
std::string get_unique_id(const std::string &prefix)
{
return get_unique_id([prefix](int nr)
{ return prefix + std::to_string(nr + 1); });
}
// --------------------------------------------------------------------
/// \brief Rename a single column in the rows that match \a cond to value \a value
/// making sure the linked categories are updated according to the link.
/// That means, child categories are updated if the links are absolute
/// and unique. If they are not, the child category rows are split.
void update_value(condition &&cond, std::string_view tag, std::string_view value)
{
auto rs = find(std::move(cond));
std::vector<row_handle> rows;
std::copy(rs.begin(), rs.end(), std::back_inserter(rows));
update_value(rows, tag, value);
}
void update_value(const std::vector<row_handle> &rows, std::string_view tag, std::string_view value);
// --------------------------------------------------------------------
/// \brief Return the index number for \a column_name
uint16_t get_column_ix(std::string_view column_name) const
......@@ -573,6 +601,8 @@ class category
}
}
row_handle create_copy(row_handle r);
struct item_column
{
std::string m_name;
......@@ -607,6 +637,7 @@ class category
const category_validator *m_cat_validator = nullptr;
std::vector<link> m_parent_links, m_child_links;
bool m_cascade = true;
uint32_t m_last_unique_num = 0;
class category_index* m_index = nullptr;
row *m_head = nullptr, *m_tail = nullptr;
};
......
......@@ -452,8 +452,9 @@ struct item_handle::item_value_as<T, std::enable_if_t<std::is_same_v<T, std::str
{
static std::string convert(const item_handle &ref)
{
std::string_view txt = ref.text();
return {txt.data(), txt.size()};
if (ref.empty())
return {};
return {ref.text().data(), ref.text().size()};
}
static int compare(const item_handle &ref, const std::string &value, bool icase)
......
......@@ -31,16 +31,10 @@
// duh.. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86164
// #include <regex>
// TODO: get rid of boost::iostreams
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/regex.hpp>
#include <cif++/CifUtils.hpp>
namespace io = boost::iostreams;
namespace cif::v2
{
......
......@@ -38,9 +38,8 @@
#include <filesystem>
#include <boost/algorithm/string.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <gzstream/gzstream.hpp>
#include <boost/logic/tribool.hpp>
#include <cif++/Cif++.hpp>
......@@ -48,8 +47,6 @@
#include <cif++/CifUtils.hpp>
#include <cif++/CifValidator.hpp>
namespace ba = boost::algorithm;
namespace io = boost::iostreams;
namespace fs = std::filesystem;
namespace cif
......@@ -622,10 +619,10 @@ bool operator==(const cif::Datablock &dbA, const cif::Datablock &dbB)
while (catA_i != catA.end() and catB_i != catB.end())
{
std::string nA = *catA_i;
ba::to_lower(nA);
toLower(nA);
std::string nB = *catB_i;
ba::to_lower(nB);
toLower(nB);
int d = nA.compare(nB);
if (d > 0)
......@@ -654,11 +651,11 @@ bool operator==(const cif::Datablock &dbA, const cif::Datablock &dbB)
{
std::cerr << "compare of datablocks failed" << std::endl;
if (not missingA.empty())
std::cerr << "Categories missing in A: " << ba::join(missingA, ", ") << std::endl
std::cerr << "Categories missing in A: " << cif::join(missingA, ", ") << std::endl
<< std::endl;
if (not missingB.empty())
std::cerr << "Categories missing in B: " << ba::join(missingB, ", ") << std::endl
std::cerr << "Categories missing in B: " << cif::join(missingB, ", ") << std::endl
<< std::endl;
result = false;
......@@ -673,10 +670,10 @@ bool operator==(const cif::Datablock &dbA, const cif::Datablock &dbB)
while (catA_i != catA.end() and catB_i != catB.end())
{
std::string nA = *catA_i;
ba::to_lower(nA);
toLower(nA);
std::string nB = *catB_i;
ba::to_lower(nB);
toLower(nB);
int d = nA.compare(nB);
if (d > 0)
......@@ -2156,7 +2153,7 @@ bool Category::isValid()
if (not mandatory.empty())
{
mValidator->reportError("In Category " + mName + " the following mandatory fields are missing: " + ba::join(mandatory, ", "), false);
mValidator->reportError("In Category " + mName + " the following mandatory fields are missing: " + cif::join(mandatory, ", "), false);
result = false;
}
......@@ -2447,12 +2444,12 @@ namespace detail
{
if (value.find('\n') != std::string::npos or width == 0 or value.length() > 132) // write as text field
{
ba::replace_all(value, "\n;", "\n\\;");
cif::replace_all(value, "\n;", "\n\\;");
if (offset > 0)
os << std::endl;
os << ';' << value;
if (not ba::ends_with(value, "\n"))
if (not cif::ends_with(value, "\n"))
os << std::endl;
os << ';' << std::endl;
offset = 0;
......@@ -3451,48 +3448,50 @@ void File::load(const std::filesystem::path &p)
{
fs::path path(p);
std::ifstream inFile(p, std::ios_base::in | std::ios_base::binary);
if (not inFile.is_open())
throw std::runtime_error("Could not open file: " + path.string());
io::filtering_stream<io::input> in;
std::string ext;
if (path.extension() == ".gz")
{
in.push(io::gzip_decompressor());
ext = path.stem().extension().string();
}
in.push(inFile);
gzstream::ifstream in(p);
try
{
load(in);
try
{
load(in);
}
catch (const std::exception &ex)
{
if (cif::VERBOSE >= 0)
std::cerr << "Error loading file " << path << std::endl;
throw;
}
}
catch (const std::exception &ex)
else
{
if (cif::VERBOSE >= 0)
std::cerr << "Error loading file " << path << std::endl;
throw;
std::ifstream inFile(p, std::ios_base::in | std::ios_base::binary);
try
{
load(inFile);
}
catch (const std::exception &ex)
{
if (cif::VERBOSE >= 0)
std::cerr << "Error loading file " << path << std::endl;
throw;
}
}
}
void File::save(const std::filesystem::path &p)
{
fs::path path(p);
std::ofstream outFile(p, std::ios_base::out | std::ios_base::binary);
io::filtering_stream<io::output> out;
if (path.extension() == ".gz")
if (p.extension() == ".gz")
{
out.push(io::gzip_compressor());
path = path.stem();
gzstream::ofstream outFile(p);
save(outFile);
}
else
{
std::ofstream outFile(p, std::ios_base::out | std::ios_base::binary);
save(outFile);
}
out.push(outFile);
save(out);
}
void File::load(std::istream &is)
......@@ -3534,14 +3533,16 @@ void File::load(const char *data, std::size_t length)
membuf(char *data, size_t length) { this->setg(data, data, data + length); }
} buffer(const_cast<char *>(data), length);
std::istream is(&buffer);
io::filtering_stream<io::input> in;
if (gzipped)
in.push(io::gzip_decompressor());
in.push(is);
load(is);
{
gzstream::istream is(&buffer);
load(is);
}
else
{
std::istream is(&buffer);
load(is);
}
}
void File::save(std::ostream &os)
......
......@@ -26,14 +26,10 @@
#include <set>
#include <boost/algorithm/string.hpp>
#include <cif++/Cif++.hpp>
#include <cif++/CifParser.hpp>
#include <cif++/CifValidator.hpp>
namespace ba = boost::algorithm;
extern int VERBOSE;
namespace cif
......@@ -1300,9 +1296,9 @@ bool DictParser::collectItemTypes()
std::string code, primitiveCode, construct;
cif::tie(code, primitiveCode, construct) = t.get("code", "primitive_code", "construct");
ba::replace_all(construct, "\\n", "\n");
ba::replace_all(construct, "\\t", "\t");
ba::replace_all(construct, "\\\n", "");
cif::replace_all(construct, "\\n", "\n");
cif::replace_all(construct, "\\t", "\t");
cif::replace_all(construct, "\\\n", "");
try
{
......
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
......@@ -25,6 +25,7 @@
*/
#include <atomic>
#include <cassert>
#include <cmath>
#include <fstream>
#include <iomanip>
......@@ -42,13 +43,10 @@
#include <termios.h>
#endif
#include <boost/algorithm/string.hpp>
#include <cif++/CifUtils.hpp>
#include "revision.hpp"
namespace ba = boost::algorithm;
namespace fs = std::filesystem;
// --------------------------------------------------------------------
......@@ -96,7 +94,7 @@ bool iequals(std::string_view a, std::string_view b)
bool result = a.length() == b.length();
for (auto ai = a.begin(), bi = b.begin(); result and ai != a.end(); ++ai, ++bi)
result = kCharToLowerMap[uint8_t(*ai)] == kCharToLowerMap[uint8_t(*bi)];
// result = tolower(*ai) == tolower(*bi);
// result = tolower(*ai) == tolower(*bi);
return result;
}
......@@ -152,7 +150,7 @@ void toLower(std::string &s)
c = tolower(c);
}
std::string toLowerCopy(const std::string &s)
std::string toLowerCopy(std::string_view s)
{
std::string result(s);
for (auto &c : result)
......@@ -160,6 +158,91 @@ std::string toLowerCopy(const std::string &s)
return result;
}
void toUpper(std::string &s)
{
for (auto &c : s)
c = toupper(c);
}
void replace_all(std::string &s, std::string_view what, std::string_view with)
{
for (std::string::size_type p = s.find(what); p != std::string::npos; p = s.find(what, p))
s.replace(p, what.length(), with);
}
bool icontains(std::string_view s, std::string_view q)
{
return contains(toLowerCopy(s), toLowerCopy(q));
}
void trim_right(std::string &s)
{
auto e = s.end();
while (e != s.begin())
{
auto pe = std::prev(e);
if (not std::isspace(*pe))
break;
e = pe;
}
if (e != s.end())
s.erase(e, s.end());
}
std::string trim_right_copy(std::string_view s)
{
auto e = s.end();
while (e != s.begin())
{
auto pe = std::prev(e);
if (not std::isspace(*pe))
break;
e = pe;
}
return {s.begin(), e};
}
std::string trim_left_copy(std::string_view s)
{
auto b = s.begin();
while (b != s.end())
{
if (not std::isspace(*b))
break;
b = std::next(b);
}
return {b, s.end()};
}
void trim_left(std::string &s)
{
auto b = s.begin();
while (b != s.end())
{
if (not std::isspace(*b))
break;
b = std::next(b);
}
s.erase(s.begin(), b);
}
void trim(std::string &s)
{
trim_right(s);
trim_left(s);
}
std::string trim_copy(std::string_view s)
{
return trim_left_copy(trim_right_copy(s));
}
// --------------------------------------------------------------------
std::tuple<std::string, std::string> splitTagName(std::string_view tag)
......@@ -181,7 +264,7 @@ std::tuple<std::string, std::string> splitTagName(std::string_view tag)
std::string cifIdForNumber(int number)
{
std::string result;
if (number >= 26 * 26 * 26)
result = 'L' + std::to_string(number);
else
......@@ -192,17 +275,17 @@ std::string cifIdForNumber(int number)
result += char('A' - 1 + v);
number %= (26 * 26);
}
if (number >= 26)
{
int v = number / 26;
result += char('A' - 1 + v);
number %= 26;
}
result += char('A' + number);
}
assert(not result.empty());
return result;
}
......@@ -433,11 +516,8 @@ std::vector<std::string> wrapLine(const std::string &text, size_t width)
std::vector<std::string> wordWrap(const std::string &text, size_t width)
{
std::vector<std::string> paragraphs;
ba::split(paragraphs, text, ba::is_any_of("\n"));
std::vector<std::string> result;
for (auto &p : paragraphs)
for (auto p : cif::split<std::string>(text, "\n"))
{
if (p.empty())
{
......@@ -482,12 +562,15 @@ std::string GetExecutablePath()
// convert from utf16 to utf8
std::wstring_convert<std::codecvt_utf8<wchar_t>> conv1;
std::string u8str = conv1.to_bytes(ws);
std::string u8str = conv1.to_bytes(ws);
return u8str;
}
#else
#include <limits.h>
uint32_t get_terminal_width()
{
uint32_t result = 80;
......@@ -786,9 +869,9 @@ struct rsrc_imp
#if _MSC_VER
extern "C" const mrsrc::rsrc_imp* gResourceIndexDefault[1] = {};
extern "C" const char* gResourceDataDefault[1] = {};
extern "C" const char* gResourceNameDefault[1] = {};
extern "C" const mrsrc::rsrc_imp *gResourceIndexDefault[1] = {};
extern "C" const char *gResourceDataDefault[1] = {};
extern "C" const char *gResourceNameDefault[1] = {};
extern "C" const mrsrc::rsrc_imp gResourceIndex[];
extern "C" const char gResourceData[];
......@@ -1250,12 +1333,14 @@ class ResourcePool
result.reset(file.release());
}
}
catch (...) {}
catch (...)
{
}
return result;
}
std::map<std::string,std::filesystem::path> mLocalResources;
std::map<std::string, std::filesystem::path> mLocalResources;
std::deque<fs::path> mDirs;
};
......@@ -1301,7 +1386,7 @@ std::unique_ptr<std::istream> ResourcePool::load(fs::path name)
result.reset(new mrsrc::istream(rsrc));
}
return result;
return result;
}
// --------------------------------------------------------------------
......
......@@ -27,17 +27,13 @@
#include <fstream>
#include <filesystem>
#include <boost/algorithm/string.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <gzstream/gzstream.hpp>
#include <cif++/Cif++.hpp>
#include <cif++/CifParser.hpp>
#include <cif++/CifValidator.hpp>
namespace ba = boost::algorithm;
namespace fs = std::filesystem;
namespace io = boost::iostreams;
extern int VERBOSE;
......@@ -416,15 +412,11 @@ const Validator &ValidatorFactory::operator[](std::string_view dictionary)
if (fs::exists(p, ec) and not ec)
{
std::ifstream file(p, std::ios::binary);
gzstream::ifstream file(p);
if (not file.is_open())
throw std::runtime_error("Could not open dictionary (" + p.string() + ")");
io::filtering_stream<io::input> in;
in.push(io::gzip_decompressor());
in.push(file);
mValidators.emplace_back(dictionary, in);
mValidators.emplace_back(dictionary, file);
}
else
throw std::runtime_error("Dictionary not found or defined (" + dict_name.string() + ")");
......
......@@ -29,8 +29,6 @@
#include <numeric>
#include <shared_mutex>
#include <boost/algorithm/string.hpp>
#include <filesystem>
#include <fstream>
......@@ -40,7 +38,6 @@
#include <cif++/Compound.hpp>
#include <cif++/Point.hpp>
namespace ba = boost::algorithm;
namespace fs = std::filesystem;
namespace mmcif
......@@ -126,7 +123,7 @@ Compound::Compound(cif::Datablock &db)
chemComp.front().get("id", "name", "type", "formula", "formula_weight", "pdbx_formal_charge");
// The name should not contain newline characters since that triggers validation errors later on
ba::replace_all(mName, "\n", "");
cif::replace_all(mName, "\n", "");
mGroup = "non-polymer";
......@@ -286,7 +283,7 @@ class CompoundFactoryImpl : public std::enable_shared_from_this<CompoundFactoryI
{
std::shared_lock lock(mMutex);
ba::to_upper(id);
cif::toUpper(id);
Compound *result = nullptr;
......@@ -554,7 +551,7 @@ CCP4CompoundFactoryImpl::CCP4CompoundFactoryImpl(const fs::path &clibd_mon, std:
{
if (std::regex_match(group, peptideRx))
mKnownPeptides.insert(threeLetterCode);
else if (ba::iequals(group, "DNA") or ba::iequals(group, "RNA"))
else if (cif::iequals(group, "DNA") or cif::iequals(group, "RNA"))
mKnownBases.insert(threeLetterCode);
}
}
......@@ -576,10 +573,10 @@ Compound *CCP4CompoundFactoryImpl::create(const std::string &id)
cif::tie(name, group, numberAtomsAll, numberAtomsNh) =
row.get("name", "group", "number_atoms_all", "number_atoms_nh");
fs::path resFile = mCLIBD_MON / ba::to_lower_copy(id.substr(0, 1)) / (id + ".cif");
fs::path resFile = mCLIBD_MON / cif::toLowerCopy(id.substr(0, 1)) / (id + ".cif");
if (not fs::exists(resFile) and (id == "COM" or id == "CON" or "PRN")) // seriously...
resFile = mCLIBD_MON / ba::to_lower_copy(id.substr(0, 1)) / (id + '_' + id + ".cif");
resFile = mCLIBD_MON / cif::toLowerCopy(id.substr(0, 1)) / (id + '_' + id + ".cif");
if (fs::exists(resFile))
{
......
......@@ -29,7 +29,6 @@
#include <map>
#include <set>
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
#include <cif++/AtomType.hpp>
......@@ -37,8 +36,6 @@
#include <cif++/PDB2CifRemark3.hpp>
#include <cif++/CifUtils.hpp>
namespace ba = boost::algorithm;
using cif::Datablock;
using cif::Category;
using cif::Row;
......@@ -992,7 +989,7 @@ std::string Remark3Parser::nextLine()
while (mRec->is("REMARK 3") and mRec->mVlen > valueIndent)
{
std::string v(mRec->mValue + 4, mRec->mValue + mRec->mVlen);
if (not ba::starts_with(v, indent))
if (not cif::starts_with(v, indent))
break;
mLine += ' ';
......@@ -1146,7 +1143,7 @@ void Remark3Parser::storeCapture(const char* category, std::initializer_list<con
++capture;
std::string value = mM[capture].str();
ba::trim(value);
cif::trim(value);
if (iequals(value, "NULL") or iequals(value, "NONE") or iequals(value, "Inf") or iequals(value, "+Inf") or iequals(value, std::string(value.length(), '*')))
continue;
......@@ -1253,7 +1250,7 @@ void Remark3Parser::storeRefineLsRestr(const char* type, std::initializer_list<c
++capture;
std::string value = mM[capture].str();
ba::trim(value);
cif::trim(value);
if (value.empty() or iequals(value, "NULL") or iequals(value, "Inf") or iequals(value, "+Inf") or iequals(value, std::string(value.length(), '*')))
continue;
......@@ -1284,7 +1281,7 @@ void Remark3Parser::updateRefineLsRestr(const char* type, std::initializer_list<
++capture;
std::string value = mM[capture].str();
ba::trim(value);
cif::trim(value);
if (iequals(value, "NULL") or iequals(value, std::string(value.length(), '*')))
value.clear();
......@@ -1385,32 +1382,29 @@ bool Remark3Parser::parse(const std::string& expMethod, PDBRecord* r, cif::Datab
}
};
for (auto p = make_split_iterator(line, ba::first_finder(", "));
not p.eof(); ++p)
for (auto program : cif::split<std::string>(line, ", ", true))
{
std::string program(p->begin(), p->end());
if (ba::starts_with(program, "BUSTER"))
if (cif::starts_with(program, "BUSTER"))
tryParser(new BUSTER_TNT_Remark3Parser(program, expMethod, r, db));
else if (ba::starts_with(program, "CNS") or ba::starts_with(program, "CNX"))
else if (cif::starts_with(program, "CNS") or cif::starts_with(program, "CNX"))
tryParser(new CNS_Remark3Parser(program, expMethod, r, db));
else if (ba::starts_with(program, "PHENIX"))
else if (cif::starts_with(program, "PHENIX"))
tryParser(new PHENIX_Remark3Parser(program, expMethod, r, db));
else if (ba::starts_with(program, "NUCLSQ"))
else if (cif::starts_with(program, "NUCLSQ"))
tryParser(new NUCLSQ_Remark3Parser(program, expMethod, r, db));
else if (ba::starts_with(program, "PROLSQ"))
else if (cif::starts_with(program, "PROLSQ"))
tryParser(new PROLSQ_Remark3Parser(program, expMethod, r, db));
else if (ba::starts_with(program, "REFMAC"))
else if (cif::starts_with(program, "REFMAC"))
{
// simply try both and take the best
tryParser(new REFMAC_Remark3Parser(program, expMethod, r, db));
tryParser(new REFMAC5_Remark3Parser(program, expMethod, r, db));
}
else if (ba::starts_with(program, "SHELXL"))
else if (cif::starts_with(program, "SHELXL"))
tryParser(new SHELXL_Remark3Parser(program, expMethod, r, db));
else if (ba::starts_with(program, "TNT"))
else if (cif::starts_with(program, "TNT"))
tryParser(new TNT_Remark3Parser(program, expMethod, r, db));
else if (ba::starts_with(program, "X-PLOR"))
else if (cif::starts_with(program, "X-PLOR"))
tryParser(new XPLOR_Remark3Parser(program, expMethod, r, db));
else if (cif::VERBOSE > 0)
std::cerr << "Skipping unknown program (" << program << ") in REMARK 3" << std::endl;
......
......@@ -30,13 +30,9 @@
#include <numeric>
#include <thread>
#include <boost/algorithm/string.hpp>
#include <cif++/Secondary.hpp>
#include <cif++/Structure.hpp>
namespace ba = boost::algorithm;
// --------------------------------------------------------------------
namespace mmcif
......@@ -106,7 +102,7 @@ const ResidueInfo kResidueInfo[] = {
ResidueType MapResidue(std::string inName)
{
ba::trim(inName);
cif::trim(inName);
ResidueType result = kUnknownResidue;
......
......@@ -31,9 +31,7 @@
#include <iomanip>
#include <numeric>
#include <boost/algorithm/string.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <gzstream/gzstream.hpp>
#if __cpp_lib_format
#include <format>
......@@ -47,8 +45,6 @@
// #include <cif++/AtomShape.hpp>
namespace fs = std::filesystem;
namespace ba = boost::algorithm;
namespace io = boost::iostreams;
extern int cif::VERBOSE;
......@@ -1289,81 +1285,27 @@ float Branch::weight() const
void File::load(const std::filesystem::path &path)
{
std::ifstream inFile(path, std::ios_base::in | std::ios_base::binary);
if (not inFile.is_open())
throw std::runtime_error("No such file: " + path.string());
io::filtering_stream<io::input> in;
std::string ext = path.extension().string();
if (path.extension() == ".gz")
if (ext == ".gz")
{
in.push(io::gzip_decompressor());
ext = path.stem().extension().string();
}
gzstream::ifstream in(path);
in.push(inFile);
ext = path.stem().extension().string();
try
{
// OK, we've got the file, now create a protein
if (ext == ".cif")
load(in);
else if (ext == ".pdb" or ext == ".ent")
if (ext == ".pdb" or ext == ".ent")
ReadPDBFile(in, *this);
else
{
try
{
if (cif::VERBOSE > 0)
std::cerr << "unrecognized file extension, trying cif" << std::endl;
cif::File::load(in);
}
catch (const cif::CifParserError &e)
{
if (cif::VERBOSE > 0)
std::cerr << "Not cif, trying plain old PDB" << std::endl;
// pffft...
in.reset();
if (inFile.is_open())
inFile.seekg(0);
else
inFile.open(path, std::ios_base::in | std::ios::binary);
if (path.extension() == ".gz")
in.push(io::gzip_decompressor());
in.push(inFile);
ReadPDBFile(in, *this);
}
}
cif::File::load(in);
}
catch (const std::exception &ex)
else
{
if (cif::VERBOSE >= 0)
std::cerr << "Error trying to load file " << path << std::endl;
throw;
}
// validate, otherwise lots of functionality won't work
loadDictionary("mmcif_pdbx_v50");
if (not isValid() and cif::VERBOSE >= 0)
std::cerr << "Invalid mmCIF file" << (cif::VERBOSE > 0 ? "." : " use --verbose option to see errors") << std::endl;
}
std::ifstream in(path, std::ios_base::binary);
void File::load(std::istream &is)
{
try
{
cif::File::load(is);
}
catch (const cif::CifParserError &e)
{
ReadPDBFile(is, *this);
if (ext == ".pdb" or ext == ".ent")
ReadPDBFile(in, *this);
else
cif::File::load(in);
}
// validate, otherwise lots of functionality won't work
......@@ -1376,21 +1318,20 @@ void File::save(const std::filesystem::path &path)
{
fs::path file = path.filename();
std::ofstream outFile(path, std::ios_base::out | std::ios_base::binary);
io::filtering_stream<io::output> out;
std::unique_ptr<std::ostream> outFile;
if (file.extension() == ".gz")
{
out.push(io::gzip_compressor());
outFile.reset(new gzstream::ofstream(path));
file.replace_extension("");
}
out.push(outFile);
else
outFile.reset(new std::ofstream(path, std::ios_base::out | std::ios_base::binary));
if (file.extension() == ".pdb")
WritePDBFile(out, data());
WritePDBFile(*outFile, data());
else
cif::File::save(out);
cif::File::save(*outFile);
}
// --------------------------------------------------------------------
......
......@@ -24,12 +24,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <boost/algorithm/string.hpp>
#include <cif++/TlsParser.hpp>
namespace ba = boost::algorithm;
namespace cif
{
......@@ -1830,7 +1826,7 @@ TLSSelectionPtr ParseSelectionDetails(const std::string& program, const std::str
TLSSelectionPtr result;
if (ba::icontains(program, "buster"))
if (cif::icontains(program, "buster"))
{
result = buster.Parse(selection);
......@@ -1848,7 +1844,7 @@ TLSSelectionPtr ParseSelectionDetails(const std::string& program, const std::str
result = phenix.Parse(selection);
}
}
else if (ba::icontains(program, "phenix"))
else if (cif::icontains(program, "phenix"))
{
result = phenix.Parse(selection);
......
......@@ -1294,9 +1294,9 @@ bool DictParser::collectItemTypes()
std::string code, primitiveCode, construct;
cif::tie(code, primitiveCode, construct) = t.get("code", "primitive_code", "construct");
ba::replace_all(construct, "\\n", "\n");
ba::replace_all(construct, "\\t", "\t");
ba::replace_all(construct, "\\\n", "");
cif::replace_all(construct, "\\n", "\n");
cif::replace_all(construct, "\\t", "\t");
cif::replace_all(construct, "\\\n", "");
try
{
......
......@@ -34,32 +34,6 @@
namespace cif::v2
{
template <typename V>
std::string join(const V &arr, std::string_view sep)
{
std::ostringstream s;
if (not arr.empty())
{
auto ai = arr.begin();
auto ni = std::next(ai);
for (;;)
{
s << *ai;
ai = ni;
ni = std::next(ai);
if (ni == arr.end())
break;
s << sep;
}
}
return s.str();
}
// --------------------------------------------------------------------
class row_comparator
......@@ -1013,8 +987,13 @@ category::iterator category::erase(iterator pos)
for (size_t ix = 0; ix < link->m_parent_keys.size(); ++ix)
{
std::string_view value = rh[link->m_parent_keys[ix]].text();
// cond = std::move(cond) and (key(link->m_child_keys[ix]) == value or key(link->m_child_keys[ix]) == null);
cond = std::move(cond) and (key(link->m_child_keys[ix]) == value);
auto childKey = link->m_child_keys[ix];
if (childCat->m_cat_validator and childCat->m_cat_validator->m_mandatory_fields.contains(childKey))
cond = std::move(cond) and key(childKey) == value;
else
cond = std::move(cond) and (key(childKey) == value or key(childKey) == null);
}
childCat->erase_orphans(std::move(cond));
......@@ -1133,6 +1112,164 @@ void category::erase_orphans(condition &&cond)
erase(iterator(*this, r));
}
std::string category::get_unique_id(std::function<std::string(int)> generator)
{
using namespace cif::v2::literals;
std::string id_tag = "id";
if (m_cat_validator != nullptr and m_cat_validator->m_keys.size() == 1)
id_tag = m_cat_validator->m_keys.front();
// calling size() often is a waste of resources
if (m_last_unique_num == 0)
m_last_unique_num = size();
for (;;)
{
std::string result = generator(static_cast<int>(m_last_unique_num++));
if (exists(key(id_tag) == result))
continue;
return result;
}
}
void category::update_value(const std::vector<row_handle> &rows, std::string_view tag, std::string_view value)
{
using namespace std::literals;
if (rows.empty())
return;
auto colIx = get_column_ix(tag);
if (colIx >= m_columns.size())
throw std::runtime_error("Invalid column " + std::string{ value } + " for " + m_name);
auto &col = m_columns[colIx];
// check the value
if (col.m_validator)
(*col.m_validator)(value);
// first some sanity checks, what was the old value and is it the same for all rows?
std::string_view oldValue = rows.front()[tag].text();
for (auto row : rows)
{
if (oldValue != row[tag].text())
throw std::runtime_error("Inconsistent old values in update_value");
}
if (oldValue == value) // no need to do anything
return;
// update rows, but do not cascade
for (auto row : rows)
row.assign(colIx, value, false);
// see if we need to update any child categories that depend on this value
for (auto parent : rows)
{
for (auto &&[childCat, linked] : m_child_links)
{
if (std::find(linked->m_parent_keys.begin(), linked->m_parent_keys.end(), tag) == linked->m_parent_keys.end())
continue;
condition cond;
std::string childTag;
for (size_t ix = 0; ix < linked->m_parent_keys.size(); ++ix)
{
std::string pk = linked->m_parent_keys[ix];
std::string ck = linked->m_child_keys[ix];
// TODO: add code to *NOT* test mandatory fields for Empty
if (pk == tag)
{
childTag = ck;
cond = std::move(cond) && key(ck) == oldValue;
}
else
cond = std::move(cond) && key(ck) == parent[pk].text();
}
auto children = childCat->find(std::move(cond));
if (children.empty())
continue;
std::vector<row_handle> child_rows;
std::copy(children.begin(), children.end(), std::back_inserter(child_rows));
// now be careful. If we search back from child to parent and still find a valid parent row
// we cannot simply rename the child but will have to create a new child. Unless that new
// child already exists of course.
std::vector<row_handle> process;
for (auto child : child_rows)
{
condition cond_c;
for (size_t ix = 0; ix < linked->m_parent_keys.size(); ++ix)
{
std::string pk = linked->m_parent_keys[ix];
std::string ck = linked->m_child_keys[ix];
// TODO: add code to *NOT* test mandatory fields for Empty
cond_c = std::move(cond_c) && key(pk) == child[ck].text();
}
auto parents = find(std::move(cond_c));
if (parents.empty())
{
process.push_back(child);
continue;
}
// oops, we need to split this child, unless a row already exists for the new value
condition check;
for (size_t ix = 0; ix < linked->m_parent_keys.size(); ++ix)
{
std::string pk = linked->m_parent_keys[ix];
std::string ck = linked->m_child_keys[ix];
// TODO: add code to *NOT* test mandatory fields for Empty
if (pk == tag)
check = std::move(check) && key(ck) == value;
else
check = std::move(check) && key(ck) == parent[pk].text();
}
if (childCat->exists(std::move(check))) // phew..., narrow escape
continue;
// create the actual copy, if we can...
if (childCat->m_cat_validator != nullptr and childCat->m_cat_validator->m_keys.size() == 1)
{
auto copy = childCat->create_copy(child);
if (copy != child)
{
process.push_back(child);
continue;
}
}
// cannot update this...
if (cif::VERBOSE > 0)
std::cerr << "Cannot update child " << childCat->m_name << "." << childTag << " with value " << value << std::endl;
}
// finally, update the children
if (not process.empty())
childCat->update_value(std::move(process), childTag, value);
}
}
}
void category::update_value(row *row, size_t column, std::string_view value, bool updateLinked, bool validate)
{
auto &col = m_columns[column];
......@@ -1235,7 +1372,7 @@ void category::update_value(row *row, size_t column, std::string_view value, boo
std::string pk = linked->m_parent_keys[ix];
std::string ck = linked->m_child_keys[ix];
// TODO add code to *NOT* test mandatory fields for Empty
// TODO: add code to *NOT* test mandatory fields for Empty
if (pk == iv->m_tag)
{
......@@ -1272,7 +1409,7 @@ void category::update_value(row *row, size_t column, std::string_view value, boo
std::string pk = linked->m_parent_keys[ix];
std::string ck = linked->m_child_keys[ix];
// TODO add code to *NOT* test mandatory fields for Empty
// TODO: add code to *NOT* test mandatory fields for Empty
if (pk == iv->m_tag)
cond_n = std::move(cond_n) and key(ck) == value;
......@@ -1301,6 +1438,40 @@ void category::update_value(row *row, size_t column, std::string_view value, boo
}
}
row_handle category::create_copy(row_handle r)
{
// copy the values
std::vector<item> items;
for (item_value *iv = r.m_row->m_head; iv != nullptr; iv = iv->m_next)
items.emplace_back(m_columns[iv->m_column_ix].m_name, iv->text());
if (m_cat_validator and m_cat_validator->m_keys.size() == 1)
{
auto key = m_cat_validator->m_keys.front();
auto kv = m_cat_validator->get_validator_for_item(key);
for (auto &item : items)
{
if (item.name() != key)
continue;
if (kv->m_type->m_primitive_type == DDL_PrimitiveType::Numb)
item.value(get_unique_id(""));
else
item.value(get_unique_id(m_name + "_id_"));
break;
}
}
return emplace(items.begin(), items.end());
// auto &&[result, inserted] = emplace(items.begin(), items.end());
// // assert(inserted);
// return result;
}
// proxy methods for every insertion
category::iterator category::insert_impl(const_iterator pos, row *n)
{
......
......@@ -28,6 +28,8 @@
#include <fstream>
#include <iostream>
#include <gzstream/gzstream.hpp>
#include <cif++/v2/dictionary_parser.hpp>
#include <cif++/v2/validate.hpp>
......@@ -407,13 +409,10 @@ const validator &validator_factory::operator[](std::string_view dictionary_name)
if (std::filesystem::exists(p, ec) and not ec)
{
std::ifstream file(p, std::ios::binary);
if (not file.is_open())
throw std::runtime_error("Could not open dictionary (" + p.string() + ")");
gzstream::ifstream in(p);
io::filtering_stream<io::input> in;
in.push(io::gzip_decompressor());
in.push(file);
if (not in.is_open())
throw std::runtime_error("Could not open dictionary (" + p.string() + ")");
construct_validator(dictionary_name, in);
}
......
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