mirror of
https://github.com/PDB-REDO/libcifpp.git
synced 2026-06-04 13:54:25 +08:00
Merge branch 'with-sqlite' into develop
This commit is contained in:
644
CMakeLists.txt
644
CMakeLists.txt
@@ -24,16 +24,18 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.23)
|
||||
|
||||
cmake_policy(SET CMP0135 NEW)
|
||||
|
||||
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
|
||||
endif()
|
||||
|
||||
# set the project name
|
||||
project(
|
||||
libcifpp
|
||||
VERSION 9.0.5
|
||||
LANGUAGES CXX C)
|
||||
libcifpp
|
||||
VERSION 10.0.0
|
||||
LANGUAGES CXX C)
|
||||
|
||||
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
@@ -47,16 +49,16 @@ include(VersionString)
|
||||
|
||||
# When building with ninja-multiconfig, build both debug and release by default
|
||||
if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config")
|
||||
set(CMAKE_CROSS_CONFIGS "Debug;Release")
|
||||
set(CMAKE_DEFAULT_CONFIGS "Debug;Release")
|
||||
set(CMAKE_CROSS_CONFIGS "Debug;Release")
|
||||
set(CMAKE_DEFAULT_CONFIGS "Debug;Release")
|
||||
endif()
|
||||
|
||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
set(CMAKE_CXX_FLAGS
|
||||
"${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers"
|
||||
)
|
||||
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()
|
||||
|
||||
# Build documentation?
|
||||
@@ -65,140 +67,149 @@ set(BUILD_DOCUMENTATION OFF CACHE BOOL "Build the documentation")
|
||||
# Optionally build a version to be installed inside CCP4
|
||||
set(BUILD_FOR_CCP4 OFF CACHE BOOL "Build a version to be installed in CCP4")
|
||||
|
||||
# Create the cql/sqlite interface
|
||||
set(BUILD_SQLITE_INTERFACE ON CACHE BOOL "Build the sqlite interface")
|
||||
|
||||
# Building shared libraries?
|
||||
if(NOT(BUILD_FOR_CCP4 AND WIN32))
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build a shared library instead of a static one")
|
||||
if(NOT (BUILD_FOR_CCP4 AND WIN32))
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build a shared library instead of a static one")
|
||||
endif()
|
||||
|
||||
if(PROJECT_IS_TOP_LEVEL AND NOT BUILD_FOR_CCP4)
|
||||
# Lots of code depend on the availability of the components.cif file
|
||||
set(CIFPP_DOWNLOAD_CCD ON CACHE BOOL "Download the CCD file components.cif during installation")
|
||||
# Lots of code depend on the availability of the components.cif file
|
||||
set(CIFPP_DOWNLOAD_CCD ON CACHE BOOL "Download the CCD file components.cif during installation")
|
||||
|
||||
# An optional cron script can be installed to keep the data files up-to-date
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(CIFPP_INSTALL_UPDATE_SCRIPT ON CACHE BOOL "Install the script to update CCD and dictionary files")
|
||||
endif()
|
||||
# An optional cron script can be installed to keep the data files up-to-date
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(CIFPP_INSTALL_UPDATE_SCRIPT ON CACHE BOOL "Install the script to update CCD and dictionary files")
|
||||
endif()
|
||||
else()
|
||||
unset(CIFPP_DOWNLOAD_CCD)
|
||||
unset(CIFPP_INSTALL_UPDATE_SCRIPT)
|
||||
unset(CIFPP_DOWNLOAD_CCD)
|
||||
unset(CIFPP_INSTALL_UPDATE_SCRIPT)
|
||||
endif()
|
||||
|
||||
# When CCP4 is sourced in the environment, we can recreate the symmetry
|
||||
# operations table
|
||||
if(EXISTS "$ENV{CCP4}/lib/data/syminfo.lib")
|
||||
set(CIFPP_RECREATE_SYMOP_DATA ON CACHE BOOL "Recreate SymOp data table in case it is out of date")
|
||||
set(CIFPP_RECREATE_SYMOP_DATA ON CACHE BOOL "Recreate SymOp data table in case it is out of date")
|
||||
endif()
|
||||
|
||||
# CCP4 build
|
||||
if(BUILD_FOR_CCP4)
|
||||
if("$ENV{CCP4}" STREQUAL "" OR NOT EXISTS $ENV{CCP4})
|
||||
message(FATAL_ERROR "cifpp: A CCP4 built was requested but CCP4 was not sourced")
|
||||
else()
|
||||
list(PREPEND CMAKE_MODULE_PATH "$ENV{CCP4}")
|
||||
list(PREPEND CMAKE_PREFIX_PATH "$ENV{CCP4}")
|
||||
set(CMAKE_INSTALL_PREFIX "$ENV{CCP4}")
|
||||
if("$ENV{CCP4}" STREQUAL "" OR NOT EXISTS $ENV{CCP4})
|
||||
message(FATAL_ERROR "cifpp: A CCP4 built was requested but CCP4 was not sourced")
|
||||
else()
|
||||
list(PREPEND CMAKE_MODULE_PATH "$ENV{CCP4}")
|
||||
list(PREPEND CMAKE_PREFIX_PATH "$ENV{CCP4}")
|
||||
set(CMAKE_INSTALL_PREFIX "$ENV{CCP4}")
|
||||
|
||||
if(WIN32)
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
endif()
|
||||
endif()
|
||||
if(WIN32)
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Now include the GNUInstallDirs module
|
||||
include(GNUInstallDirs)
|
||||
|
||||
if(WIN32)
|
||||
if(${CMAKE_SYSTEM_VERSION} GREATER_EQUAL 10) # Windows 10
|
||||
add_definitions(-D _WIN32_WINNT=0x0A00)
|
||||
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.3) # Windows 8.1
|
||||
add_definitions(-D _WIN32_WINNT=0x0603)
|
||||
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.2) # Windows 8
|
||||
add_definitions(-D _WIN32_WINNT=0x0602)
|
||||
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.1) # Windows 7
|
||||
add_definitions(-D _WIN32_WINNT=0x0601)
|
||||
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.0) # Windows Vista
|
||||
add_definitions(-D _WIN32_WINNT=0x0600)
|
||||
else() # Windows XP (5.1)
|
||||
add_definitions(-D _WIN32_WINNT=0x0501)
|
||||
endif()
|
||||
if(${CMAKE_SYSTEM_VERSION} GREATER_EQUAL 10) # Windows 10
|
||||
add_definitions(-D _WIN32_WINNT=0x0A00)
|
||||
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.3) # Windows 8.1
|
||||
add_definitions(-D _WIN32_WINNT=0x0603)
|
||||
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.2) # Windows 8
|
||||
add_definitions(-D _WIN32_WINNT=0x0602)
|
||||
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.1) # Windows 7
|
||||
add_definitions(-D _WIN32_WINNT=0x0601)
|
||||
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.0) # Windows Vista
|
||||
add_definitions(-D _WIN32_WINNT=0x0600)
|
||||
else() # Windows XP (5.1)
|
||||
add_definitions(-D _WIN32_WINNT=0x0501)
|
||||
endif()
|
||||
|
||||
# We do not want to write an export file for all our symbols...
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
# We do not want to write an export file for all our symbols...
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
# make msvc standards compliant...
|
||||
add_compile_options(/permissive- /bigobj)
|
||||
add_link_options(/NODEFAULTLIB:library)
|
||||
# make msvc standards compliant...
|
||||
add_compile_options(/permissive- /bigobj)
|
||||
add_link_options(/NODEFAULTLIB:library)
|
||||
|
||||
# This is dubious...
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
|
||||
else()
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
endif()
|
||||
# This is dubious...
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
|
||||
else()
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Libraries
|
||||
|
||||
if(MSVC)
|
||||
# Avoid linking the shared library of zlib. Search ZLIB_ROOT first if it is
|
||||
# set.
|
||||
if(ZLIB_ROOT)
|
||||
set(_ZLIB_SEARCH_ROOT PATHS ${ZLIB_ROOT} NO_DEFAULT_PATH)
|
||||
list(APPEND _ZLIB_SEARCHES _ZLIB_SEARCH_ROOT)
|
||||
endif()
|
||||
# Avoid linking the shared library of zlib. Search ZLIB_ROOT first if it is
|
||||
# set.
|
||||
if(ZLIB_ROOT)
|
||||
set(_ZLIB_SEARCH_ROOT PATHS ${ZLIB_ROOT} NO_DEFAULT_PATH)
|
||||
list(APPEND _ZLIB_SEARCHES _ZLIB_SEARCH_ROOT)
|
||||
endif()
|
||||
|
||||
# Normal search.
|
||||
set(_ZLIB_x86 "(x86)")
|
||||
set(_ZLIB_SEARCH_NORMAL
|
||||
PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\GnuWin32\\Zlib;InstallPath]"
|
||||
"$ENV{ProgramFiles}/zlib" "$ENV{ProgramFiles${_ZLIB_x86}}/zlib")
|
||||
unset(_ZLIB_x86)
|
||||
list(APPEND _ZLIB_SEARCHES _ZLIB_SEARCH_NORMAL)
|
||||
# Normal search.
|
||||
set(_ZLIB_x86 "(x86)")
|
||||
set(_ZLIB_SEARCH_NORMAL
|
||||
PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\GnuWin32\\Zlib;InstallPath]"
|
||||
"$ENV{ProgramFiles}/zlib" "$ENV{ProgramFiles${_ZLIB_x86}}/zlib")
|
||||
unset(_ZLIB_x86)
|
||||
list(APPEND _ZLIB_SEARCHES _ZLIB_SEARCH_NORMAL)
|
||||
|
||||
if(BUILD_FOR_CCP4)
|
||||
list(PREPEND _ZLIB_SEARCHES "$ENV{CCP4}/lib")
|
||||
endif()
|
||||
if(BUILD_FOR_CCP4)
|
||||
list(PREPEND _ZLIB_SEARCHES "$ENV{CCP4}/lib")
|
||||
endif()
|
||||
|
||||
foreach(search ${_ZLIB_SEARCHES})
|
||||
find_library(
|
||||
ZLIB_LIBRARY
|
||||
NAMES zlibstatic NAMES_PER_DIR ${${search}}
|
||||
PATH_SUFFIXES lib)
|
||||
endforeach()
|
||||
foreach(search ${_ZLIB_SEARCHES})
|
||||
find_library(
|
||||
ZLIB_LIBRARY
|
||||
NAMES zlibstatic NAMES_PER_DIR ${${search}}
|
||||
PATH_SUFFIXES lib)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
# Using fast_float for float parsing, but only if needed
|
||||
try_compile(STD_CHARCONV_COMPILING
|
||||
SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/test-charconv.cpp)
|
||||
SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/test-charconv.cpp
|
||||
CXX_STANDARD 20
|
||||
CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if(NOT STD_CHARCONV_COMPILING)
|
||||
message(NOTICE "libcifpp: Using fast_float for std::from_chars")
|
||||
FetchContent_Declare(fastfloat
|
||||
GIT_REPOSITORY "https://github.com/fastfloat/fast_float"
|
||||
GIT_TAG v8.0.2
|
||||
EXCLUDE_FROM_ALL)
|
||||
FetchContent_MakeAvailable(fastfloat)
|
||||
find_package(FastFloat 8.0 QUIET CONFIG)
|
||||
if(NOT FastFloat_FOUND)
|
||||
message(STATUS "FastFloat not found in system, fetching from GitHub")
|
||||
FetchContent_Declare(fastfloat
|
||||
GIT_REPOSITORY "https://github.com/fastfloat/fast_float"
|
||||
GIT_TAG v8.0.2
|
||||
EXCLUDE_FROM_ALL)
|
||||
FetchContent_MakeAvailable(fastfloat)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(Threads)
|
||||
find_package(ZLIB QUIET)
|
||||
|
||||
if(NOT ZLIB_FOUND)
|
||||
message(FATAL_ERROR "cifpp: The zlib development files were not found you this system, please install them and try again (hint: on debian/ubuntu use apt-get install zlib1g-dev)")
|
||||
message(FATAL_ERROR "cifpp: The zlib development files were not found you this system, please install them and try again (hint: on debian/ubuntu use apt-get install zlib1g-dev)")
|
||||
endif()
|
||||
|
||||
include(FindPkgConfig)
|
||||
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PCRE2 IMPORTED_TARGET libpcre2-8)
|
||||
pkg_check_modules(PCRE2 IMPORTED_TARGET libpcre2-8)
|
||||
endif()
|
||||
|
||||
if(NOT PCRE2_FOUND)
|
||||
add_subdirectory(pcre2-simple)
|
||||
add_subdirectory(pcre2-simple)
|
||||
endif()
|
||||
|
||||
# Using Eigen3 is a bit of a thing. We don't want to build it completely since
|
||||
@@ -207,355 +218,362 @@ endif()
|
||||
find_package(Eigen3 3.4 QUIET)
|
||||
|
||||
if(Eigen3_FOUND AND TARGET Eigen3::Eigen)
|
||||
get_target_property(EIGEN_INCLUDE_DIR Eigen3::Eigen
|
||||
INTERFACE_INCLUDE_DIRECTORIES)
|
||||
get_target_property(EIGEN_INCLUDE_DIR Eigen3::Eigen
|
||||
INTERFACE_INCLUDE_DIRECTORIES)
|
||||
else()
|
||||
# Use ExternalProject since FetchContent always tries to install the result...
|
||||
ExternalProject_Add(my-eigen3
|
||||
URL https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.zip
|
||||
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND "")
|
||||
# Use ExternalProject since FetchContent always tries to install the result...
|
||||
ExternalProject_Add(my-eigen3
|
||||
URL https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.zip
|
||||
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND "")
|
||||
|
||||
ExternalProject_Get_Property(my-eigen3 SOURCE_DIR)
|
||||
set(EIGEN_INCLUDE_DIR ${SOURCE_DIR})
|
||||
ExternalProject_Get_Property(my-eigen3 SOURCE_DIR)
|
||||
set(EIGEN_INCLUDE_DIR ${SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
# SymOp data table
|
||||
if(CIFPP_RECREATE_SYMOP_DATA)
|
||||
# The tool to create the table
|
||||
add_executable(symop-map-generator
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/symop-map-generator.cpp")
|
||||
# The tool to create the table
|
||||
add_executable(symop-map-generator
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/symop-map-generator.cpp")
|
||||
|
||||
target_compile_features(symop-map-generator PUBLIC cxx_std_20)
|
||||
target_compile_features(symop-map-generator PUBLIC cxx_std_20)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/symop_table_data.hpp
|
||||
COMMAND
|
||||
$<TARGET_FILE:symop-map-generator> $ENV{CLIBD}/syminfo.lib
|
||||
$ENV{CLIBD}/symop.lib ${CMAKE_CURRENT_SOURCE_DIR}/src/symop_table_data.hpp)
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/symop_table_data.hpp
|
||||
COMMAND
|
||||
$<TARGET_FILE:symop-map-generator> $ENV{CLIBD}/syminfo.lib
|
||||
$ENV{CLIBD}/symop.lib ${CMAKE_CURRENT_SOURCE_DIR}/src/symop_table_data.hpp)
|
||||
|
||||
add_custom_target(
|
||||
OUTPUT
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/symop_table_data.hpp
|
||||
DEPENDS symop-map-generator "$ENV{CLIBD}/syminfo.lib"
|
||||
"$ENV{CLIBD}/symop.lib")
|
||||
add_custom_target(
|
||||
OUTPUT
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/symop_table_data.hpp
|
||||
DEPENDS symop-map-generator "$ENV{CLIBD}/syminfo.lib"
|
||||
"$ENV{CLIBD}/symop.lib")
|
||||
endif()
|
||||
|
||||
# Create a revision file, containing the current git version info
|
||||
write_version_header("${CMAKE_CURRENT_SOURCE_DIR}/src/" LIB_NAME "LibCIFPP")
|
||||
|
||||
# Sources
|
||||
set(project_sources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/category.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/condition.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/datablock.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/dictionary_parser.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/file.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/item.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/parser.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/row.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/validate.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/text.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/utilities.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/atom_type.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/compound.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/point.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/symmetry.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/model.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/cif2pdb.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb_record.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/reconstruct.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/validate-pdbx.cpp
|
||||
list(APPEND project_sources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/category.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/condition.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/datablock.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/dictionary_parser.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/file.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/item.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/parser.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/row.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/validate.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/text.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/utilities.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/atom_type.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/compound.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/point.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/symmetry.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/model.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/cql.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/cif2pdb.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb_record.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif_remark_3.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/reconstruct.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/validate-pdbx.cpp
|
||||
)
|
||||
|
||||
set(project_headers
|
||||
include/cif++.hpp
|
||||
include/cif++/atom_type.hpp
|
||||
include/cif++/category.hpp
|
||||
include/cif++/compound.hpp
|
||||
include/cif++/condition.hpp
|
||||
include/cif++/datablock.hpp
|
||||
include/cif++/dictionary_parser.hpp
|
||||
include/cif++/exports.hpp
|
||||
include/cif++/file.hpp
|
||||
include/cif++/format.hpp
|
||||
include/cif++/forward_decl.hpp
|
||||
include/cif++/gzio.hpp
|
||||
include/cif++/item.hpp
|
||||
include/cif++/iterator.hpp
|
||||
include/cif++/matrix.hpp
|
||||
include/cif++/model.hpp
|
||||
include/cif++/parser.hpp
|
||||
include/cif++/pdb/cif2pdb.hpp
|
||||
include/cif++/pdb.hpp
|
||||
include/cif++/pdb/io.hpp
|
||||
include/cif++/pdb/pdb2cif.hpp
|
||||
include/cif++/pdb/tls.hpp
|
||||
include/cif++/point.hpp
|
||||
include/cif++/row.hpp
|
||||
include/cif++/symmetry.hpp
|
||||
include/cif++/text.hpp
|
||||
include/cif++/utilities.hpp
|
||||
include/cif++/validate.hpp
|
||||
if(BUILD_SQLITE_INTERFACE)
|
||||
list(APPEND project_sources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/sqlite3/sqlite3.c)
|
||||
endif()
|
||||
|
||||
list(APPEND project_headers
|
||||
include/cif++.hpp
|
||||
include/cif++/atom_type.hpp
|
||||
include/cif++/category.hpp
|
||||
include/cif++/compound.hpp
|
||||
include/cif++/condition.hpp
|
||||
include/cif++/datablock.hpp
|
||||
include/cif++/dictionary_parser.hpp
|
||||
include/cif++/exports.hpp
|
||||
include/cif++/file.hpp
|
||||
include/cif++/format.hpp
|
||||
include/cif++/forward_decl.hpp
|
||||
include/cif++/gzio.hpp
|
||||
include/cif++/item.hpp
|
||||
include/cif++/iterator.hpp
|
||||
include/cif++/matrix.hpp
|
||||
include/cif++/model.hpp
|
||||
include/cif++/parser.hpp
|
||||
include/cif++/pdb/cif2pdb.hpp
|
||||
include/cif++/pdb.hpp
|
||||
include/cif++/pdb/io.hpp
|
||||
include/cif++/pdb/pdb2cif.hpp
|
||||
include/cif++/pdb/tls.hpp
|
||||
include/cif++/point.hpp
|
||||
include/cif++/row.hpp
|
||||
include/cif++/symmetry.hpp
|
||||
include/cif++/text.hpp
|
||||
include/cif++/cql.hpp
|
||||
include/cif++/utilities.hpp
|
||||
include/cif++/validate.hpp
|
||||
)
|
||||
|
||||
add_library(cifpp)
|
||||
add_library(cifpp::cifpp ALIAS cifpp)
|
||||
|
||||
if(TARGET my-eigen3)
|
||||
add_dependencies(cifpp my-eigen3)
|
||||
add_dependencies(cifpp my-eigen3)
|
||||
endif()
|
||||
|
||||
target_sources(cifpp
|
||||
PRIVATE ${project_sources}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/symop_table_data.hpp
|
||||
PUBLIC
|
||||
FILE_SET cifpp_headers TYPE HEADERS
|
||||
BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
FILES ${project_headers}
|
||||
PRIVATE ${project_sources}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/symop_table_data.hpp
|
||||
PUBLIC
|
||||
FILE_SET cifpp_headers TYPE HEADERS
|
||||
BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
FILES ${project_headers}
|
||||
)
|
||||
|
||||
# The code now really requires C++20
|
||||
target_compile_features(cifpp PUBLIC cxx_std_20)
|
||||
|
||||
generate_export_header(cifpp EXPORT_FILE_NAME
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/cif++/exports.hpp)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/cif++/exports.hpp)
|
||||
|
||||
if(MSVC)
|
||||
target_compile_definitions(cifpp PUBLIC NOMINMAX=1)
|
||||
target_compile_definitions(cifpp PUBLIC NOMINMAX=1)
|
||||
endif()
|
||||
|
||||
set_target_properties(cifpp PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
target_include_directories(
|
||||
cifpp
|
||||
PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
PRIVATE "${EIGEN_INCLUDE_DIR}")
|
||||
cifpp
|
||||
PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
PRIVATE "${EIGEN_INCLUDE_DIR}")
|
||||
|
||||
target_link_libraries(cifpp
|
||||
PUBLIC Threads::Threads ZLIB::ZLIB $<$<TARGET_EXISTS:std::atomic>:std::atomic>)
|
||||
PUBLIC Threads::Threads ZLIB::ZLIB $<$<TARGET_EXISTS:std::atomic>:std::atomic>)
|
||||
|
||||
if(PCRE2_FOUND)
|
||||
target_include_directories(cifpp PRIVATE ${PCRE2_INCLUDE_DIRS})
|
||||
target_link_libraries(cifpp PRIVATE ${PCRE2_LINK_LIBRARIES})
|
||||
target_include_directories(cifpp PRIVATE ${PCRE2_INCLUDE_DIRS})
|
||||
target_link_libraries(cifpp PRIVATE ${PCRE2_LINK_LIBRARIES})
|
||||
else()
|
||||
target_link_libraries(cifpp PRIVATE $<BUILD_INTERFACE:pcre2s>)
|
||||
target_link_libraries(cifpp PRIVATE $<BUILD_INTERFACE:pcre2s>)
|
||||
endif()
|
||||
|
||||
if(NOT STD_CHARCONV_COMPILING)
|
||||
target_link_libraries(cifpp PRIVATE FastFloat::fast_float)
|
||||
target_link_libraries(cifpp PRIVATE FastFloat::fast_float)
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
target_link_options(cifpp PRIVATE -undefined dynamic_lookup)
|
||||
target_link_options(cifpp PRIVATE -undefined dynamic_lookup)
|
||||
endif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
|
||||
if(CIFPP_DOWNLOAD_CCD)
|
||||
# download the components.cif file from CCD
|
||||
set(COMPONENTS_CIF ${CMAKE_CURRENT_SOURCE_DIR}/rsrc/components.cif)
|
||||
# download the components.cif file from CCD
|
||||
set(COMPONENTS_CIF ${CMAKE_CURRENT_SOURCE_DIR}/rsrc/components.cif)
|
||||
|
||||
if(EXISTS ${COMPONENTS_CIF})
|
||||
file(SIZE ${COMPONENTS_CIF} CCD_FILE_SIZE)
|
||||
if(EXISTS ${COMPONENTS_CIF})
|
||||
file(SIZE ${COMPONENTS_CIF} CCD_FILE_SIZE)
|
||||
|
||||
if(CCD_FILE_SIZE EQUAL 0)
|
||||
message(STATUS "cifpp: Removing empty ${COMPONENTS_CIF} file")
|
||||
file(REMOVE "${COMPONENTS_CIF}")
|
||||
endif()
|
||||
endif()
|
||||
if(CCD_FILE_SIZE EQUAL 0)
|
||||
message(STATUS "cifpp: Removing empty ${COMPONENTS_CIF} file")
|
||||
file(REMOVE "${COMPONENTS_CIF}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS ${COMPONENTS_CIF})
|
||||
# Since the file(DOWNLOAD) command in cmake does not use compression, we try
|
||||
# to download the gzipped version and decompress it ourselves.
|
||||
find_program(GUNZIP gunzip)
|
||||
if(NOT EXISTS ${COMPONENTS_CIF})
|
||||
# Since the file(DOWNLOAD) command in cmake does not use compression, we try
|
||||
# to download the gzipped version and decompress it ourselves.
|
||||
find_program(GUNZIP gunzip)
|
||||
|
||||
if(WIN32 OR GUNZIP STREQUAL "GUNZIP-NOTFOUND")
|
||||
file(
|
||||
DOWNLOAD https://files.wwpdb.org/pub/pdb/data/monomers/components.cif
|
||||
${COMPONENTS_CIF}
|
||||
SHOW_PROGRESS
|
||||
STATUS CCD_FETCH_STATUS)
|
||||
else()
|
||||
if(NOT EXISTS "${COMPONENTS_CIF}.gz")
|
||||
file(
|
||||
DOWNLOAD
|
||||
https://files.wwpdb.org/pub/pdb/data/monomers/components.cif.gz
|
||||
${COMPONENTS_CIF}.gz
|
||||
SHOW_PROGRESS
|
||||
STATUS CCD_FETCH_STATUS)
|
||||
endif()
|
||||
if(WIN32 OR GUNZIP STREQUAL "GUNZIP-NOTFOUND")
|
||||
file(
|
||||
DOWNLOAD https://files.wwpdb.org/pub/pdb/data/monomers/components.cif
|
||||
${COMPONENTS_CIF}
|
||||
SHOW_PROGRESS
|
||||
STATUS CCD_FETCH_STATUS)
|
||||
else()
|
||||
if(NOT EXISTS "${COMPONENTS_CIF}.gz")
|
||||
file(
|
||||
DOWNLOAD
|
||||
https://files.wwpdb.org/pub/pdb/data/monomers/components.cif.gz
|
||||
${COMPONENTS_CIF}.gz
|
||||
SHOW_PROGRESS
|
||||
STATUS CCD_FETCH_STATUS)
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${COMPONENTS_CIF}
|
||||
COMMAND "${GUNZIP}" ${COMPONENTS_CIF}.gz
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/rsrc/)
|
||||
add_custom_command(
|
||||
OUTPUT ${COMPONENTS_CIF}
|
||||
COMMAND "${GUNZIP}" ${COMPONENTS_CIF}.gz
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/rsrc/)
|
||||
|
||||
add_custom_target(COMPONENTS ALL DEPENDS ${COMPONENTS_CIF})
|
||||
endif()
|
||||
add_custom_target(COMPONENTS ALL DEPENDS ${COMPONENTS_CIF})
|
||||
endif()
|
||||
|
||||
# Do not continue if downloading went wrong
|
||||
list(POP_FRONT CCD_FETCH_STATUS CCD_FETCH_STATUS_CODE)
|
||||
# Do not continue if downloading went wrong
|
||||
list(POP_FRONT CCD_FETCH_STATUS CCD_FETCH_STATUS_CODE)
|
||||
|
||||
if(CCD_FETCH_STATUS_CODE)
|
||||
message(
|
||||
FATAL_ERROR "cifpp: Error trying to download CCD file: ${CCD_FETCH_STATUS}")
|
||||
endif()
|
||||
endif()
|
||||
if(CCD_FETCH_STATUS_CODE)
|
||||
message(
|
||||
FATAL_ERROR "cifpp: Error trying to download CCD file: ${CCD_FETCH_STATUS}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Installation directories
|
||||
if(BUILD_FOR_CCP4)
|
||||
set(CIFPP_DATA_DIR
|
||||
"$ENV{CCP4}/share/libcifpp"
|
||||
CACHE PATH "Directory where dictionary and other static data is stored")
|
||||
set(CIFPP_DATA_DIR
|
||||
"$ENV{CCP4}/share/libcifpp"
|
||||
CACHE PATH "Directory where dictionary and other static data is stored")
|
||||
else()
|
||||
set(CIFPP_DATA_DIR
|
||||
"${CMAKE_INSTALL_FULL_DATADIR}/libcifpp"
|
||||
CACHE PATH "Directory where dictionary and other static data is stored")
|
||||
set(CIFPP_DATA_DIR
|
||||
"${CMAKE_INSTALL_FULL_DATADIR}/libcifpp"
|
||||
CACHE PATH "Directory where dictionary and other static data is stored")
|
||||
endif()
|
||||
|
||||
if(CIFPP_DATA_DIR)
|
||||
target_compile_definitions(cifpp PUBLIC DATA_DIR="${CIFPP_DATA_DIR}")
|
||||
set_target_properties(cifpp PROPERTIES CIFPP_DATA_DIR ${CIFPP_DATA_DIR})
|
||||
target_compile_definitions(cifpp PUBLIC DATA_DIR="${CIFPP_DATA_DIR}")
|
||||
set_target_properties(cifpp PROPERTIES CIFPP_DATA_DIR ${CIFPP_DATA_DIR})
|
||||
endif()
|
||||
|
||||
if(NOT PROJECT_IS_TOP_LEVEL)
|
||||
set(CIFPP_SHARE_DIR ${CIFPP_DATA_DIR} PARENT_SCOPE)
|
||||
set(CIFPP_SHARE_DIR ${CIFPP_DATA_DIR} PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT BUILD_FOR_CCP4)
|
||||
if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr/local")
|
||||
set(CIFPP_CACHE_DIR
|
||||
"/var/cache/libcifpp"
|
||||
CACHE PATH "The directory where downloaded data files are stored")
|
||||
else()
|
||||
set(CIFPP_CACHE_DIR
|
||||
"${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/cache/libcifpp"
|
||||
CACHE PATH "The directory where downloaded data files are stored")
|
||||
endif()
|
||||
if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr/local")
|
||||
set(CIFPP_CACHE_DIR
|
||||
"/var/cache/libcifpp"
|
||||
CACHE PATH "The directory where downloaded data files are stored")
|
||||
else()
|
||||
set(CIFPP_CACHE_DIR
|
||||
"${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/cache/libcifpp"
|
||||
CACHE PATH "The directory where downloaded data files are stored")
|
||||
endif()
|
||||
|
||||
target_compile_definitions(cifpp PUBLIC CACHE_DIR="${CIFPP_CACHE_DIR}")
|
||||
target_compile_definitions(cifpp PUBLIC CACHE_DIR="${CIFPP_CACHE_DIR}")
|
||||
|
||||
set(CIFPP_ETC_DIR
|
||||
"${CMAKE_INSTALL_FULL_SYSCONFDIR}"
|
||||
CACHE PATH "The directory where the update configuration file is stored")
|
||||
set(CIFPP_ETC_DIR
|
||||
"${CMAKE_INSTALL_FULL_SYSCONFDIR}"
|
||||
CACHE PATH "The directory where the update configuration file is stored")
|
||||
else()
|
||||
unset(CIFPP_CACHE_DIR)
|
||||
unset(CIFPP_CACHE_DIR)
|
||||
endif()
|
||||
|
||||
# Install rules
|
||||
install(TARGETS cifpp
|
||||
EXPORT cifpp
|
||||
FILE_SET cifpp_headers DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
EXPORT cifpp
|
||||
FILE_SET cifpp_headers DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
||||
if(MSVC AND BUILD_SHARED_LIBS)
|
||||
install(
|
||||
FILES $<TARGET_PDB_FILE:cifpp>
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
OPTIONAL)
|
||||
install(
|
||||
FILES $<TARGET_PDB_FILE:cifpp>
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
OPTIONAL)
|
||||
endif()
|
||||
|
||||
# Clean up old config files (with old names)
|
||||
file(GLOB OLD_CONFIG_FILES
|
||||
${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppConfig*.cmake
|
||||
${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppTargets*.cmake)
|
||||
${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppConfig*.cmake
|
||||
${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cifpp/cifppTargets*.cmake)
|
||||
|
||||
if(OLD_CONFIG_FILES)
|
||||
message(
|
||||
STATUS "cifpp: Installation will remove old config files: ${OLD_CONFIG_FILES}")
|
||||
install(CODE "file(REMOVE ${OLD_CONFIG_FILES})")
|
||||
message(
|
||||
STATUS "cifpp: Installation will remove old config files: ${OLD_CONFIG_FILES}")
|
||||
install(CODE "file(REMOVE ${OLD_CONFIG_FILES})")
|
||||
endif()
|
||||
|
||||
install(EXPORT cifpp
|
||||
NAMESPACE cifpp::
|
||||
FILE "cifpp-targets.cmake"
|
||||
DESTINATION lib/cmake/cifpp)
|
||||
NAMESPACE cifpp::
|
||||
FILE "cifpp-targets.cmake"
|
||||
DESTINATION lib/cmake/cifpp)
|
||||
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_ddl.dic
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_ma.dic
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/libcifpp)
|
||||
FILES ${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_ddl.dic
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_pdbx.dic
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/rsrc/mmcif_ma.dic
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/libcifpp)
|
||||
|
||||
if(CIFPP_DATA_DIR AND CIFPP_DOWNLOAD_CCD)
|
||||
install(FILES ${COMPONENTS_CIF}
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/libcifpp)
|
||||
install(FILES ${COMPONENTS_CIF}
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/libcifpp)
|
||||
endif()
|
||||
|
||||
set(CONFIG_TEMPLATE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cifpp-config.cmake.in)
|
||||
|
||||
configure_package_config_file(
|
||||
${CONFIG_TEMPLATE_FILE} ${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake
|
||||
INSTALL_DESTINATION lib/cmake/cifpp
|
||||
PATH_VARS CIFPP_DATA_DIR)
|
||||
${CONFIG_TEMPLATE_FILE} ${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake
|
||||
INSTALL_DESTINATION lib/cmake/cifpp
|
||||
PATH_VARS CIFPP_DATA_DIR)
|
||||
|
||||
install(
|
||||
FILES "${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config-version.cmake"
|
||||
DESTINATION lib/cmake/cifpp)
|
||||
FILES "${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config-version.cmake"
|
||||
DESTINATION lib/cmake/cifpp)
|
||||
|
||||
set_target_properties(
|
||||
cifpp
|
||||
PROPERTIES VERSION ${PROJECT_VERSION}
|
||||
SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
|
||||
INTERFACE_cifpp_MAJOR_VERSION ${PROJECT_VERSION_MAJOR})
|
||||
cifpp
|
||||
PROPERTIES VERSION ${PROJECT_VERSION}
|
||||
SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
|
||||
INTERFACE_cifpp_MAJOR_VERSION ${PROJECT_VERSION_MAJOR})
|
||||
|
||||
set_property(
|
||||
TARGET cifpp
|
||||
APPEND
|
||||
PROPERTY COMPATIBLE_INTERFACE_STRING cifpp_MAJOR_VERSION)
|
||||
TARGET cifpp
|
||||
APPEND
|
||||
PROPERTY COMPATIBLE_INTERFACE_STRING cifpp_MAJOR_VERSION)
|
||||
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config-version.cmake"
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion)
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/cifpp/cifpp-config-version.cmake"
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion)
|
||||
|
||||
if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL)
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
# Optionally install the update scripts for CCD and dictionary files
|
||||
if(CIFPP_INSTALL_UPDATE_SCRIPT)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tools/update-libcifpp-data.in
|
||||
update-libcifpp-data @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tools/update-libcifpp-data.in
|
||||
update-libcifpp-data @ONLY)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
|
||||
${CMAKE_SYSTEM_NAME} STREQUAL "GNU" OR
|
||||
${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/update-libcifpp-data
|
||||
DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/cron.weekly
|
||||
PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE
|
||||
WORLD_READ)
|
||||
else()
|
||||
message(FATAL_ERROR "cifpp: Don't know where to install the update script")
|
||||
endif()
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
|
||||
${CMAKE_SYSTEM_NAME} STREQUAL "GNU" OR
|
||||
${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/update-libcifpp-data
|
||||
DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/cron.weekly
|
||||
PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE
|
||||
WORLD_READ)
|
||||
else()
|
||||
message(FATAL_ERROR "cifpp: Don't know where to install the update script")
|
||||
endif()
|
||||
|
||||
# a config file, to make it complete
|
||||
# install(DIRECTORY DESTINATION "${CMAKE_INSTALL_LOCALSTATEDIR}/libcifpp")
|
||||
if(NOT EXISTS "${CMAKE_INSTALL_SYSCONFDIR}/libcifpp.conf")
|
||||
file(
|
||||
WRITE ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.conf
|
||||
[[# Uncomment the next line to enable automatic updates
|
||||
# a config file, to make it complete
|
||||
# install(DIRECTORY DESTINATION "${CMAKE_INSTALL_LOCALSTATEDIR}/libcifpp")
|
||||
if(NOT EXISTS "${CMAKE_INSTALL_SYSCONFDIR}/libcifpp.conf")
|
||||
file(
|
||||
WRITE ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.conf
|
||||
[[# Uncomment the next line to enable automatic updates
|
||||
# update=true
|
||||
]])
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.conf
|
||||
DESTINATION ${CMAKE_INSTALL_SYSCONFDIR})
|
||||
install(
|
||||
CODE "message(\"cifpp: A configuration file has been written to ${CIFPP_ETC_DIR}/libcifpp.conf, please edit this file to enable automatic updates\")"
|
||||
)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libcifpp.conf
|
||||
DESTINATION ${CMAKE_INSTALL_SYSCONFDIR})
|
||||
install(
|
||||
CODE "message(\"cifpp: A configuration file has been written to ${CIFPP_ETC_DIR}/libcifpp.conf, please edit this file to enable automatic updates\")"
|
||||
)
|
||||
|
||||
install(DIRECTORY DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/libcifpp/cache-update.d)
|
||||
endif()
|
||||
install(DIRECTORY DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/libcifpp/cache-update.d)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(cifpp PUBLIC CACHE_DIR="${CIFPP_CACHE_DIR}")
|
||||
target_compile_definitions(cifpp PUBLIC CACHE_DIR="${CIFPP_CACHE_DIR}")
|
||||
endif()
|
||||
|
||||
if(BUILD_DOCUMENTATION)
|
||||
add_subdirectory(docs)
|
||||
add_subdirectory(docs)
|
||||
endif()
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
Version 10.0.0
|
||||
- Added a SQLite interface.
|
||||
|
||||
Version 9.0.5
|
||||
- Added exists to compound_factory
|
||||
- Added sub_matrix, fix and extend determinant calculation
|
||||
|
||||
Binary file not shown.
@@ -26,15 +26,12 @@
|
||||
|
||||
#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"
|
||||
|
||||
#include <array>
|
||||
|
||||
/** \file category.hpp
|
||||
* Documentation for the cif::category class
|
||||
*
|
||||
@@ -181,6 +178,27 @@ class category
|
||||
|
||||
const std::string &name() const { return m_name; } ///< Returns the name of the category
|
||||
|
||||
/// \brief Rename category to @a new_name
|
||||
void name(std::string_view new_name)
|
||||
{
|
||||
m_name = new_name;
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
/// \brief Return true if the category has been modified since last open/save
|
||||
constexpr bool is_dirty() const
|
||||
{
|
||||
return m_dirty;
|
||||
}
|
||||
|
||||
/// \brief Mark the category as modified according to @a dirty
|
||||
void set_dirty(bool dirty)
|
||||
{
|
||||
m_dirty = dirty;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
[[deprecated("use key_items instead")]] iset key_fields() const; ///< Returns the cif::iset of key item names. Retrieved from the @ref category_validator for this category
|
||||
|
||||
iset key_items() const; ///< Returns the cif::iset of key item names. Retrieved from the @ref category_validator for this category
|
||||
@@ -1055,60 +1073,6 @@ class category
|
||||
{ return value; });
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Naming used to be very inconsistent. For backward compatibility,
|
||||
// the old function names are here as deprecated variants.
|
||||
|
||||
/// \brief Return the index number for \a column_name
|
||||
[[deprecated("Use get_item_ix instead")]] uint16_t get_column_ix(std::string_view column_name) const
|
||||
{
|
||||
return get_item_ix(column_name);
|
||||
}
|
||||
|
||||
/// @brief Return the name for column with index @a ix
|
||||
/// @param ix The index number
|
||||
/// @return The name of the column
|
||||
[[deprecated("use get_item_name instead")]] std::string_view get_column_name(uint16_t ix) const
|
||||
{
|
||||
return get_item_name(ix);
|
||||
}
|
||||
|
||||
/// @brief Make sure a item with name @a item_name is known and return its index number
|
||||
/// @param item_name The name of the item
|
||||
/// @return The index number of the item
|
||||
[[deprecated("use add_item instead")]] uint16_t add_column(std::string_view item_name)
|
||||
{
|
||||
return add_item(item_name);
|
||||
}
|
||||
|
||||
/** @brief Remove column name @a colum_name
|
||||
* @param column_name The column to be removed
|
||||
*/
|
||||
[[deprecated("use remove_item instead")]] void remove_column(std::string_view column_name)
|
||||
{
|
||||
remove_item(column_name);
|
||||
}
|
||||
|
||||
/** @brief Rename column @a from_name to @a to_name */
|
||||
[[deprecated("use rename_item instead")]] void rename_column(std::string_view from_name, std::string_view to_name)
|
||||
{
|
||||
rename_item(from_name, to_name);
|
||||
}
|
||||
|
||||
/// @brief Return whether a column with name @a name exists in this category
|
||||
/// @param name The name of the column
|
||||
/// @return True if the column exists
|
||||
[[deprecated("use has_item instead")]] bool has_column(std::string_view name) const
|
||||
{
|
||||
return has_item(name);
|
||||
}
|
||||
|
||||
/// @brief Return the cif::iset of columns in this category
|
||||
[[deprecated("use get_items instead")]] iset get_columns() const
|
||||
{
|
||||
return get_items();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
/// \brief Return the index number for \a item_name
|
||||
|
||||
@@ -1135,6 +1099,9 @@ class category
|
||||
*/
|
||||
void remove_item(std::string_view item_name);
|
||||
|
||||
/// \brief Drop items in this category that contain empty values in all rows.
|
||||
void drop_empty_items();
|
||||
|
||||
/** @brief Rename item @a from_name to @a to_name */
|
||||
void rename_item(std::string_view from_name, std::string_view to_name);
|
||||
|
||||
@@ -1146,8 +1113,14 @@ class category
|
||||
return get_item_ix(name) < m_items.size();
|
||||
}
|
||||
|
||||
/// @brief Return the cif::iset of items in this category
|
||||
iset get_items() const;
|
||||
/// @brief Return the items in this category
|
||||
std::vector<std::string> get_items() const;
|
||||
|
||||
/// @brief Return the number of items (colums) in this category
|
||||
size_t get_item_count() const noexcept
|
||||
{
|
||||
return m_items.size();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
@@ -1177,16 +1150,48 @@ 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
|
||||
box, // table with unicode line characters
|
||||
};
|
||||
|
||||
/// @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, bool header) 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, bool ascii) const;
|
||||
|
||||
public:
|
||||
/// friend function to make it possible to do:
|
||||
@@ -1286,6 +1291,8 @@ class category
|
||||
uint32_t m_last_unique_num = 0;
|
||||
class category_index *m_index = nullptr;
|
||||
row *m_head = nullptr, *m_tail = nullptr;
|
||||
|
||||
bool m_dirty = false; // Keep track of modifications
|
||||
};
|
||||
|
||||
} // namespace cif
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cif++/text.hpp"
|
||||
#include "cif++/row.hpp"
|
||||
#include "cif++/format.hpp"
|
||||
|
||||
@@ -1076,6 +1077,16 @@ condition operator==(const key &key, const T &v)
|
||||
return condition(new detail::key_equals_number_condition_impl(key.m_item_name, static_cast<double>(v)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Operator to create an not-equals condition based on a key @a key and a numeric value @a v
|
||||
*/
|
||||
template <Numeric T>
|
||||
condition operator!=(const key &key, const T &v)
|
||||
{
|
||||
// TODO: change key_equals_etc... to use std::variant<double,int64_t> or something
|
||||
return condition(new detail::not_condition_impl(key == v));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Operator to create an equals condition based on a key @a key and a value @a value
|
||||
*/
|
||||
|
||||
444
include/cif++/cql.hpp
Normal file
444
include/cif++/cql.hpp
Normal file
@@ -0,0 +1,444 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2025 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
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cif++/category.hpp"
|
||||
#include "cif++/datablock.hpp"
|
||||
#include "cif++/item.hpp"
|
||||
#include "cif++/iterator.hpp"
|
||||
#include "cif++/row.hpp"
|
||||
#include "cif++/validate.hpp"
|
||||
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
namespace cif::cql
|
||||
{
|
||||
|
||||
class result;
|
||||
class row;
|
||||
class transaction;
|
||||
class connection;
|
||||
|
||||
struct result_impl;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
class field_ref final
|
||||
{
|
||||
public:
|
||||
std::string_view name() const &
|
||||
{
|
||||
return m_row.get_category().get_item_name(m_index);
|
||||
}
|
||||
|
||||
constexpr size_t num() const noexcept
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
std::string_view text() const &
|
||||
{
|
||||
return m_row[m_index].text();
|
||||
}
|
||||
|
||||
/** Return the contents of this item as type @tparam T */
|
||||
template <typename T = std::string>
|
||||
auto as() const -> T
|
||||
{
|
||||
return m_row[m_index].as<T>();
|
||||
}
|
||||
|
||||
/** Return the contents of this item as type @tparam T or, if not
|
||||
* set, use @a dv as the default value.
|
||||
*/
|
||||
template <typename T>
|
||||
auto value_or(const T &dv) const
|
||||
{
|
||||
return m_row[m_index].value_or(dv);
|
||||
}
|
||||
|
||||
field_ref(row_handle rh, int col, std::shared_ptr<result_impl> result_impl)
|
||||
: m_row(rh)
|
||||
, m_index(col)
|
||||
, m_result_impl(result_impl)
|
||||
{
|
||||
}
|
||||
|
||||
field_ref(const field_ref &) = default;
|
||||
field_ref(field_ref &&) = default;
|
||||
|
||||
field_ref &operator=(const field_ref &) = default;
|
||||
field_ref &operator=(field_ref &&) = default;
|
||||
|
||||
private:
|
||||
row_handle m_row;
|
||||
int m_index;
|
||||
|
||||
std::shared_ptr<result_impl> m_result_impl;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
class row_ref final
|
||||
{
|
||||
public:
|
||||
class const_field_iterator
|
||||
{
|
||||
public:
|
||||
friend class result;
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = const field_ref;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
|
||||
const_field_iterator(const const_field_iterator &) = default;
|
||||
const_field_iterator(const_field_iterator &&) = default;
|
||||
|
||||
const_field_iterator &operator=(const const_field_iterator &) = default;
|
||||
const_field_iterator &operator=(const_field_iterator &&) = default;
|
||||
|
||||
reference operator*()
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
pointer operator->()
|
||||
{
|
||||
return &m_current;
|
||||
}
|
||||
|
||||
const_field_iterator &operator++()
|
||||
{
|
||||
if (m_row)
|
||||
{
|
||||
++m_col;
|
||||
m_current = field_ref(m_row, m_col, m_result_impl);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_field_iterator operator++(int)
|
||||
{
|
||||
const_field_iterator result(*this);
|
||||
this->operator++();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool operator==(const const_field_iterator &rhs) const
|
||||
{
|
||||
return m_row == rhs.m_row and m_col == rhs.m_col;
|
||||
}
|
||||
|
||||
bool operator!=(const const_field_iterator &rhs) const
|
||||
{
|
||||
return m_row != rhs.m_row or m_col != rhs.m_col;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class row_ref;
|
||||
|
||||
const_field_iterator(row_handle row, int column, std::shared_ptr<result_impl> result_impl)
|
||||
: m_row(row)
|
||||
, m_col(column)
|
||||
, m_current(m_row, m_col, result_impl)
|
||||
, m_result_impl(result_impl)
|
||||
{
|
||||
}
|
||||
|
||||
row_handle m_row;
|
||||
int m_col;
|
||||
field_ref m_current;
|
||||
|
||||
std::shared_ptr<result_impl> m_result_impl;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
row_ref() = default;
|
||||
|
||||
row_ref(row_handle rh, std::shared_ptr<result_impl> result_impl)
|
||||
: m_row(rh)
|
||||
, m_result_impl(result_impl)
|
||||
{
|
||||
}
|
||||
|
||||
row_ref(const row_ref &) = default;
|
||||
row_ref &operator=(const row_ref &) = default;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
const_field_iterator cbegin() const noexcept { return const_field_iterator(m_row, 0, m_result_impl); }
|
||||
const_field_iterator begin() const noexcept { return const_field_iterator(m_row, 0, m_result_impl); }
|
||||
const_field_iterator cend() const noexcept { return const_field_iterator(m_row, size(), m_result_impl); }
|
||||
const_field_iterator end() const noexcept { return const_field_iterator(m_row, size(), m_result_impl); }
|
||||
|
||||
field_ref front() const noexcept { return field_ref(m_row, 0, m_result_impl); }
|
||||
field_ref back() const noexcept { return field_ref(m_row, size() - 1, m_result_impl); }
|
||||
|
||||
size_t size() const noexcept;
|
||||
bool empty() const noexcept { return size() == 0; }
|
||||
|
||||
field_ref operator[](size_t index) const noexcept { return field_ref(m_row, index, m_result_impl); }
|
||||
field_ref operator[](std::string_view name) const;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
bool operator==(const row_ref &rhs) const { return m_row == rhs.m_row; }
|
||||
bool operator!=(const row_ref &rhs) const { return m_row != rhs.m_row; }
|
||||
|
||||
private:
|
||||
row_handle m_row;
|
||||
std::shared_ptr<result_impl> m_result_impl;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
class result
|
||||
{
|
||||
public:
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
class iterator
|
||||
{
|
||||
public:
|
||||
friend class view;
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = const row_ref;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
|
||||
// const_row_iterator() = default;
|
||||
|
||||
iterator(std::shared_ptr<result_impl> result_impl, category::iterator cat_iter)
|
||||
: m_iter(cat_iter)
|
||||
, m_current(*m_iter, result_impl)
|
||||
, m_result_impl(result_impl)
|
||||
{
|
||||
}
|
||||
|
||||
iterator(const iterator &) = default;
|
||||
iterator(iterator &&) = default;
|
||||
|
||||
// const_row_iterator &operator=(const const_row_iterator &) = default;
|
||||
// const_row_iterator &operator=(const_row_iterator &&) = default;
|
||||
|
||||
reference operator*()
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
pointer operator->()
|
||||
{
|
||||
return &m_current;
|
||||
}
|
||||
|
||||
iterator &operator++()
|
||||
{
|
||||
++m_iter;
|
||||
m_current = { *m_iter, m_result_impl };
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int)
|
||||
{
|
||||
iterator result(*this);
|
||||
this->operator++();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool operator==(const iterator &rhs) const
|
||||
{
|
||||
return m_result_impl == rhs.m_result_impl and m_iter == rhs.m_iter;
|
||||
}
|
||||
|
||||
bool operator!=(const iterator &rhs) const
|
||||
{
|
||||
return m_result_impl != rhs.m_result_impl or m_iter != rhs.m_iter;
|
||||
}
|
||||
|
||||
private:
|
||||
category::iterator m_iter;
|
||||
row_ref m_current;
|
||||
std::shared_ptr<result_impl> m_result_impl;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
result() = delete;
|
||||
result(result const &rhs) noexcept = default;
|
||||
result(result &&rhs) noexcept = default;
|
||||
result &operator=(result const &rhs) noexcept = default;
|
||||
result &operator=(result &&rhs) noexcept = default;
|
||||
|
||||
result(category &&data, const std::string &query = "");
|
||||
|
||||
~result() = default;
|
||||
|
||||
row_ref one_row() const
|
||||
{
|
||||
if (size() != 1)
|
||||
throw std::runtime_error("Expected one row");
|
||||
return front();
|
||||
}
|
||||
|
||||
field_ref one_field() const
|
||||
{
|
||||
expect_columns(1);
|
||||
|
||||
if (size() != 1)
|
||||
throw std::runtime_error("Expected one row");
|
||||
|
||||
return one_row().front();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
iterator begin() const noexcept;
|
||||
iterator cbegin() const noexcept;
|
||||
|
||||
iterator end() const noexcept;
|
||||
iterator cend() const noexcept;
|
||||
|
||||
row_ref front() const;
|
||||
row_ref back() const;
|
||||
|
||||
size_t size() const noexcept;
|
||||
bool empty() const noexcept { return size() == 0; }
|
||||
|
||||
size_t column_count() const;
|
||||
|
||||
category &get_category() const;
|
||||
|
||||
result expect_columns(size_t cols) const
|
||||
{
|
||||
if (auto actual = column_count(); cols != actual)
|
||||
throw std::runtime_error("Unexpected number of columns");
|
||||
return *this;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &os, const result &r)
|
||||
{
|
||||
os << r.get_category();
|
||||
return os;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class transaction;
|
||||
friend class SelectStatement;
|
||||
|
||||
std::shared_ptr<result_impl> m_impl;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
template <typename... Ts>
|
||||
class cql_iterator_proxy : public cif::iterator_proxy<category, Ts...>
|
||||
{
|
||||
public:
|
||||
cql_iterator_proxy(result &&res)
|
||||
: cif::iterator_proxy<category, Ts...>(res.get_category())
|
||||
, m_result(std::forward<result>(res))
|
||||
{
|
||||
m_result.expect_columns(cif::iterator_proxy<category, Ts...>::N);
|
||||
}
|
||||
|
||||
private:
|
||||
result m_result;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
class transaction final
|
||||
{
|
||||
public:
|
||||
transaction(connection &conn);
|
||||
~transaction();
|
||||
|
||||
transaction(const transaction &) = delete;
|
||||
transaction &operator=(const transaction &) = delete;
|
||||
|
||||
/// \brief Execute the sql in @a query returning an iterable result
|
||||
result exec(std::string query);
|
||||
|
||||
/// \brief Execute the sql in @a query returning an iterable result.
|
||||
/// Updates @a tail with what remains after the first statement in @a query
|
||||
result exec(std::string query, std::string &tail);
|
||||
|
||||
template<typename... Ts>
|
||||
cql_iterator_proxy<Ts...> stream(const std::string &sql)
|
||||
{
|
||||
return cql_iterator_proxy<Ts...>{ exec(sql) };
|
||||
}
|
||||
|
||||
void commit();
|
||||
void rollback();
|
||||
|
||||
private:
|
||||
connection &m_conn;
|
||||
bool m_transaction_active = false;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
class connection final
|
||||
{
|
||||
public:
|
||||
connection(datablock &db);
|
||||
~connection();
|
||||
|
||||
friend class transaction;
|
||||
|
||||
/// \brief Return true if the string @a sql contains a complete statement.
|
||||
bool is_complete_statement(const std::string &sql) const;
|
||||
|
||||
/// \brief Execute the sql in @a query returning an iterable result
|
||||
result exec(std::string query);
|
||||
|
||||
/// \brief Execute the sql in @a query returning an iterable result.
|
||||
/// Updates @a tail with what remains after the first statement in @a query
|
||||
result exec(std::string query, std::string &tail);
|
||||
|
||||
/// \brief Return true if the underlying data was modified by any query.
|
||||
bool is_modified() const;
|
||||
|
||||
private:
|
||||
struct connection_impl *m_impl;
|
||||
};
|
||||
|
||||
} // namespace cif::cql
|
||||
@@ -106,6 +106,12 @@ class datablock : public std::list<category>
|
||||
*/
|
||||
void load_dictionary();
|
||||
|
||||
/**
|
||||
* @brief Attempt to load the dictionary @a dict
|
||||
*
|
||||
*/
|
||||
void load_dictionary(std::string_view dict);
|
||||
|
||||
/**
|
||||
* @brief Set the validator object to @a v
|
||||
*
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
@@ -229,7 +228,7 @@ class item
|
||||
void value(std::string_view v) { m_value = v; }
|
||||
|
||||
/// \brief empty means either null or unknown
|
||||
bool empty() const { return m_value.empty(); }
|
||||
bool empty() const { return is_null() or is_unknown() or m_value.empty(); }
|
||||
|
||||
/// \brief returns true if the item contains '.'
|
||||
bool is_null() const { return m_value == "."; }
|
||||
@@ -260,8 +259,8 @@ class item
|
||||
// --------------------------------------------------------------------
|
||||
/// \brief the internal storage for items in a category
|
||||
///
|
||||
/// Internal storage, strictly forward linked list with minimal space
|
||||
/// requirements. Strings of size 7 or shorter are stored internally.
|
||||
/// Internal storage, with minimal space requirements. Strings of
|
||||
/// size 7 or shorter are stored internally.
|
||||
/// Typically, more than 99% of the strings in an mmCIF file are less
|
||||
/// than 8 bytes in length.
|
||||
|
||||
@@ -338,7 +337,8 @@ struct item_value
|
||||
/** Return the content of the item as a std::string_view */
|
||||
constexpr inline std::string_view text() const
|
||||
{
|
||||
return { m_length >= kBufferSize ? m_data : m_local_data, m_length };
|
||||
const char *ptr = m_length >= kBufferSize ? m_data : m_local_data;
|
||||
return (m_length == 1 and *ptr == '?') ? std::string_view{} : std::string_view{ ptr, m_length };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -26,9 +26,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cif++/condition.hpp"
|
||||
#include "cif++/row.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <numeric>
|
||||
|
||||
/**
|
||||
* @file iterator.hpp
|
||||
@@ -262,6 +265,11 @@ class iterator_impl<Category>
|
||||
return m_current;
|
||||
}
|
||||
|
||||
int64_t row_id() const
|
||||
{
|
||||
return reinterpret_cast<int64_t>(m_current.m_row);
|
||||
}
|
||||
|
||||
iterator_impl &operator++()
|
||||
{
|
||||
if (m_current)
|
||||
@@ -489,6 +497,9 @@ class iterator_proxy
|
||||
std::swap(m_item_ix, rhs.m_item_ix);
|
||||
}
|
||||
|
||||
protected:
|
||||
iterator_proxy(category_type &cat);
|
||||
|
||||
private:
|
||||
category_type *m_category;
|
||||
row_iterator m_begin, m_end;
|
||||
@@ -530,6 +541,7 @@ class conditional_iterator_proxy
|
||||
using pointer = value_type *;
|
||||
using reference = value_type;
|
||||
|
||||
conditional_iterator_impl() = default;
|
||||
conditional_iterator_impl(CategoryType &cat, row_iterator pos, const condition &cond, const std::array<uint16_t, N> &cix);
|
||||
conditional_iterator_impl(const conditional_iterator_impl &i) = default;
|
||||
conditional_iterator_impl &operator=(const conditional_iterator_impl &i) = default;
|
||||
@@ -649,6 +661,15 @@ iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat, row_iterator pos,
|
||||
m_item_ix[i++] = m_category->get_item_ix(item);
|
||||
}
|
||||
|
||||
template <typename Category, typename... Ts>
|
||||
iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat)
|
||||
: m_category(&cat)
|
||||
, m_begin(cat.begin())
|
||||
, m_end(cat.end())
|
||||
{
|
||||
std::iota(m_item_ix.begin(), m_item_ix.end(), 0);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
template <typename Category, typename... Ts>
|
||||
@@ -661,6 +682,8 @@ conditional_iterator_proxy<Category, Ts...>::conditional_iterator_impl::conditio
|
||||
{
|
||||
if (m_condition == nullptr or m_condition->empty())
|
||||
m_begin = m_end;
|
||||
else
|
||||
m_current = *m_begin;
|
||||
}
|
||||
|
||||
template <typename Category, typename... Ts>
|
||||
|
||||
@@ -149,6 +149,23 @@ void fixup_pdbx(file &pdbx_file, const validator &v);
|
||||
|
||||
bool reconstruct_pdbx(file &pdbx_file, const validator &v);
|
||||
|
||||
/** \brief This is an extension to cif::validator, use the logic in common
|
||||
* PDBx files to see if the file is internally consistent.
|
||||
*
|
||||
* This function for now checks if the following categories are consistent:
|
||||
*
|
||||
* atom_site -> pdbx_poly_seq_scheme -> entity_poly_seq -> entity_poly -> entity
|
||||
*
|
||||
* Use the common \ref cif::VERBOSE flag to turn on diagnostic messages.
|
||||
*
|
||||
* This function throws a std::system_error in case of an error
|
||||
*
|
||||
* \param pdbx_file The input file
|
||||
* \result Returns true if the file was valid and consistent
|
||||
*/
|
||||
|
||||
bool is_valid_pdbx_file(const file &pdbx_file);
|
||||
|
||||
/** \brief This is an extension to cif::validator, use the logic in common
|
||||
* PDBx files to see if the file is internally consistent.
|
||||
*
|
||||
@@ -165,8 +182,7 @@ bool reconstruct_pdbx(file &pdbx_file, const validator &v);
|
||||
* \result Returns true if the file was valid and consistent
|
||||
*/
|
||||
|
||||
bool is_valid_pdbx_file(const file &pdbx_file,
|
||||
const validator &v = validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
bool is_valid_pdbx_file(const file &pdbx_file, const validator &v);
|
||||
|
||||
/** \brief This is an extension to cif::validator, use the logic in common
|
||||
* PDBx files to see if the file is internally consistent.
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/*-
|
||||
* 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
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/// \file cif2pdb.hpp
|
||||
/// \deprecated This file is no longer used. Please use "cif++/pdb.hpp" instead
|
||||
|
||||
#warning "Use of this file is deprecated, please use "cif++/pdb.hpp"
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 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
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/// \file io.hpp
|
||||
/// \deprecated This file is no longer used. Please use "cif++/pdb.hpp" instead
|
||||
|
||||
#warning "Use of this file is deprecated, please use "cif++/pdb.hpp"
|
||||
@@ -1,32 +0,0 @@
|
||||
/*-
|
||||
* 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
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/// \file pdb2cif.hpp
|
||||
/// \deprecated This file is no longer used. Please use "cif++/pdb.hpp" instead
|
||||
|
||||
#warning "Use of this file is deprecated, please use "cif++/pdb.hpp"
|
||||
@@ -1,32 +0,0 @@
|
||||
/*-
|
||||
* 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
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/// \file tls.hpp
|
||||
/// \deprecated This code has been moved to libpdb-redo
|
||||
|
||||
#warning "This code has been moved to libpdb-redo"
|
||||
@@ -29,55 +29,61 @@
|
||||
#include "cif++/item.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @file row.hpp
|
||||
*
|
||||
*
|
||||
* The class cif::row should be an opaque type. It is used to store the
|
||||
* internal data per row in a category. You should use cif::row_handle
|
||||
* to get access to the contents in a row.
|
||||
*
|
||||
*
|
||||
* One could think of rows as vectors of cif::item. But internally
|
||||
* that's not the case.
|
||||
*
|
||||
*
|
||||
* You can access the values of stored items by name or index.
|
||||
* The return value of operator[] is an cif::item_handle object.
|
||||
*
|
||||
*
|
||||
* @code {.cpp}
|
||||
* cif::category &atom_site = my_db["atom_site"];
|
||||
* cif::row_handle rh = atom_site.front();
|
||||
*
|
||||
*
|
||||
* // by name:
|
||||
* std::string name = rh["label_atom_id"].as<std::string>();
|
||||
*
|
||||
*
|
||||
* // by index:
|
||||
* uint16_t ix = atom_site.get_item_ix("label_atom_id");
|
||||
* assert(rh[ix].as<std::string() == name);
|
||||
* @endcode
|
||||
*
|
||||
*
|
||||
* There some template magic here to allow easy extracting of data
|
||||
* from rows. This can be done using cif::tie e.g.:
|
||||
*
|
||||
*
|
||||
* @code {.cpp}
|
||||
* std::string name;
|
||||
* float x, y, z;
|
||||
*
|
||||
*
|
||||
* cif::tie(name, x, y, z) = rh.get("label_atom_id", "cartn_x", "cartn_y", "cartn_z");
|
||||
* @endcode
|
||||
*
|
||||
*
|
||||
* However, a more modern way uses structured binding:
|
||||
*
|
||||
*
|
||||
* @code {.cpp}
|
||||
* const auto &[name, x, y, z] = rh.get<std::string,float,float,float>("label_atom_id", "cartn_x", "cartn_y", "cartn_z");
|
||||
* @endcode
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace cif
|
||||
{
|
||||
|
||||
namespace cql
|
||||
{
|
||||
struct connection_impl;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
@@ -141,7 +147,7 @@ namespace detail
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// \brief similar to std::tie, assign values to each element in @a v from the
|
||||
/// \brief similar to std::tie, assign values to each element in @a v from the
|
||||
/// result of a get on a row_handle.
|
||||
template <typename... Ts>
|
||||
auto tie(Ts &...v)
|
||||
@@ -160,7 +166,7 @@ class row : public std::vector<item_value>
|
||||
/**
|
||||
* @brief Return the item_value pointer for item at index @a ix
|
||||
*/
|
||||
item_value* get(uint16_t ix)
|
||||
item_value *get(uint16_t ix)
|
||||
{
|
||||
return ix < size() ? &data()[ix] : nullptr;
|
||||
}
|
||||
@@ -168,7 +174,7 @@ class row : public std::vector<item_value>
|
||||
/**
|
||||
* @brief Return the const item_value pointer for item at index @a ix
|
||||
*/
|
||||
const item_value* get(uint16_t ix) const
|
||||
const item_value *get(uint16_t ix) const
|
||||
{
|
||||
return ix < size() ? &data()[ix] : nullptr;
|
||||
}
|
||||
@@ -184,7 +190,7 @@ class row : public std::vector<item_value>
|
||||
{
|
||||
if (ix >= size())
|
||||
resize(ix + 1);
|
||||
|
||||
|
||||
at(ix) = std::move(iv);
|
||||
}
|
||||
|
||||
@@ -208,7 +214,8 @@ class row_handle
|
||||
friend class category;
|
||||
friend class category_index;
|
||||
friend class row_initializer;
|
||||
template <typename, typename...> friend class iterator_impl;
|
||||
template <typename, typename...>
|
||||
friend class iterator_impl;
|
||||
|
||||
row_handle() = default;
|
||||
|
||||
@@ -233,6 +240,12 @@ class row_handle
|
||||
return *m_category;
|
||||
}
|
||||
|
||||
/// \brief return the row ID
|
||||
int64_t row_id() const
|
||||
{
|
||||
return reinterpret_cast<int64_t>(m_row);
|
||||
}
|
||||
|
||||
/// \brief Return true if the row is empty or uninitialised
|
||||
bool empty() const
|
||||
{
|
||||
@@ -299,19 +312,19 @@ class row_handle
|
||||
}
|
||||
|
||||
/// \brief assign each of the items named in @a values to their respective value
|
||||
void assign(const std::vector<item> &values)
|
||||
void assign(const std::vector<item> &values, bool updateLinked = true)
|
||||
{
|
||||
for (auto &value : values)
|
||||
assign(value, true);
|
||||
assign(value, updateLinked);
|
||||
}
|
||||
|
||||
/** \brief assign the value @a value to the item named @a name
|
||||
*
|
||||
/** \brief assign the value @a value to the item named @a name
|
||||
*
|
||||
* If updateLinked it true, linked records are updated as well.
|
||||
* That means that if item @a name is part of the link definition
|
||||
* and the link results in a linked record in another category
|
||||
* this record in the linked category is updated as well.
|
||||
*
|
||||
*
|
||||
* If validate is true, which is default, the assigned value is
|
||||
* checked to see if it conforms to the rules defined in the dictionary
|
||||
*/
|
||||
@@ -322,12 +335,12 @@ class row_handle
|
||||
}
|
||||
|
||||
/** \brief assign the value @a value to item at index @a item
|
||||
*
|
||||
*
|
||||
* If updateLinked it true, linked records are updated as well.
|
||||
* That means that if item @a item is part of the link definition
|
||||
* and the link results in a linked record in another category
|
||||
* this record in the linked category is updated as well.
|
||||
*
|
||||
*
|
||||
* If validate is true, which is default, the assigned value is
|
||||
* checked to see if it conforms to the rules defined in the dictionary
|
||||
*/
|
||||
@@ -346,6 +359,8 @@ class row_handle
|
||||
|
||||
uint16_t add_item(std::string_view name);
|
||||
|
||||
friend cql::connection_impl;
|
||||
|
||||
row *get_row()
|
||||
{
|
||||
return m_row;
|
||||
@@ -371,7 +386,7 @@ class row_handle
|
||||
|
||||
/**
|
||||
* @brief The class row_initializer is a list of cif::item's.
|
||||
*
|
||||
*
|
||||
* This class is used to construct new rows, it allows to
|
||||
* group a list of item name and value pairs and pass it
|
||||
* in one go to the constructing function.
|
||||
@@ -406,7 +421,6 @@ class row_initializer : public std::vector<item>
|
||||
/// \brief constructor taking the values of an existing row
|
||||
row_initializer(row_handle rh);
|
||||
|
||||
|
||||
/// \brief set the value for item name @a name to @a value
|
||||
void set_value(std::string_view name, std::string_view value);
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "cif++/text.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <filesystem>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
@@ -343,11 +342,11 @@ struct item_validator
|
||||
*/
|
||||
struct category_validator
|
||||
{
|
||||
std::string m_name; ///< The name of the category
|
||||
std::vector<std::string> m_keys; ///< The list of items that make up the key
|
||||
cif::iset m_groups; ///< The category groups this category belongs to
|
||||
cif::iset m_mandatory_items; ///< The mandatory items for this category
|
||||
std::set<item_validator> m_item_validators; ///< The item validators for the items in this category
|
||||
std::string m_name; ///< The name of the category
|
||||
std::vector<std::string> m_keys; ///< The list of items that make up the key
|
||||
cif::iset m_groups; ///< The category groups this category belongs to
|
||||
cif::iset m_mandatory_items; ///< The mandatory items for this category
|
||||
std::vector<item_validator> m_item_validators; ///< The item validators for the items in this category
|
||||
|
||||
/// @brief return true if this category sorts before @a rhs
|
||||
bool operator<(const category_validator &rhs) const
|
||||
@@ -520,10 +519,18 @@ class validator_factory
|
||||
static validator_factory &instance();
|
||||
|
||||
/// @brief Return validator with info recorded in @a audit_conform
|
||||
const validator &get(const category &audit_conform);
|
||||
const validator *get(const category &audit_conform);
|
||||
|
||||
/// @brief Return the single-file validator with name @a dictionary_name
|
||||
const validator &get(std::string_view dictionary_name);
|
||||
/// and the dictionary name may be a set of dictionaries separated by comma
|
||||
const validator *get(std::string_view dictionary_name);
|
||||
|
||||
/// @brief Return validator with info recorded in @a audit_conform
|
||||
const validator &operator[](const category &audit_conform);
|
||||
|
||||
/// @brief Return the single-file validator with name @a dictionary_name
|
||||
/// and the dictionary name may be a set of dictionaries separated by comma
|
||||
const validator &operator[](std::string_view dictionary_name);
|
||||
|
||||
/// @brief Return true if the version @a found is equal or higher than @a expected for dictionary @a name
|
||||
static bool check_version(std::string_view name, std::string_view expected, std::string_view found);
|
||||
@@ -535,6 +542,21 @@ class validator_factory
|
||||
return m_validators.emplace_back(std::move(v));
|
||||
}
|
||||
|
||||
#if __cplusplus >= 202302L
|
||||
/// @brief Return validator with info recorded in @a audit_conform
|
||||
static validator &operator[](const category &audit_conform)
|
||||
{
|
||||
return instance()[audit_conform];
|
||||
}
|
||||
|
||||
/// @brief Return the single-file validator with name @a dictionary_name
|
||||
/// and the dictionary name may be a set of dictionaries separated by comma
|
||||
static validator &operator[](std::string_view dict)
|
||||
{
|
||||
return instance()[dict];
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
validator_factory() = default;
|
||||
|
||||
|
||||
2080
sql-92.bnf
Normal file
2080
sql-92.bnf
Normal file
File diff suppressed because it is too large
Load Diff
505
src/category.cpp
505
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,8 @@
|
||||
|
||||
#include <numeric>
|
||||
#include <stack>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
// TODO: Find out what the rules are exactly for linked items, the current implementation
|
||||
// is inconsistent. It all depends whether a link is satified if a item taking part in the
|
||||
@@ -108,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)
|
||||
@@ -535,6 +538,7 @@ void swap(category &a, category &b) noexcept
|
||||
std::swap(a.m_index, b.m_index);
|
||||
std::swap(a.m_head, b.m_head);
|
||||
std::swap(a.m_tail, b.m_tail);
|
||||
std::swap(a.m_dirty, b.m_dirty);
|
||||
}
|
||||
|
||||
category::~category()
|
||||
@@ -606,6 +610,30 @@ void category::remove_item(std::string_view item_name)
|
||||
}
|
||||
}
|
||||
|
||||
void category::drop_empty_items()
|
||||
{
|
||||
std::vector<bool> is_empty(m_items.size(), true);
|
||||
|
||||
for (auto &row : *this)
|
||||
{
|
||||
for (size_t ix = 0; ix < m_items.size(); ++ix)
|
||||
{
|
||||
if (is_empty[ix] and not row[ix].empty())
|
||||
is_empty[ix] = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> items;
|
||||
for (size_t ix = 0; ix < m_items.size(); ++ix)
|
||||
{
|
||||
if (is_empty[ix])
|
||||
items.push_back(m_items[ix].m_name);
|
||||
}
|
||||
|
||||
for (auto &item : items)
|
||||
remove_item(item);
|
||||
}
|
||||
|
||||
void category::rename_item(std::string_view from_name, std::string_view to_name)
|
||||
{
|
||||
for (std::size_t ix = 0; ix < m_items.size(); ++ix)
|
||||
@@ -620,12 +648,12 @@ void category::rename_item(std::string_view from_name, std::string_view to_name)
|
||||
}
|
||||
}
|
||||
|
||||
iset category::get_items() const
|
||||
std::vector<std::string> category::get_items() const
|
||||
{
|
||||
iset result;
|
||||
std::vector<std::string> result;
|
||||
|
||||
for (auto &col : m_items)
|
||||
result.insert(col.m_name);
|
||||
result.emplace_back(col.m_name);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1248,6 +1276,7 @@ void category::clear()
|
||||
|
||||
delete m_index;
|
||||
m_index = nullptr;
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void category::erase_orphans(condition &&cond, category &parent)
|
||||
@@ -1496,6 +1525,8 @@ void category::update_value(row *row, uint16_t item, std::string_view value, boo
|
||||
if (value == oldValue) // no need to update
|
||||
return;
|
||||
|
||||
m_dirty = true;
|
||||
|
||||
std::string oldStrValue{ oldValue };
|
||||
|
||||
// check the value
|
||||
@@ -1637,6 +1668,8 @@ void category::delete_row(row *r)
|
||||
row_allocator_type ra(get_allocator());
|
||||
row_allocator_traits::destroy(ra, r);
|
||||
row_allocator_traits::deallocate(ra, r, 1);
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1737,6 +1770,8 @@ category::iterator category::insert_impl(const_iterator pos, row *n)
|
||||
n = n->m_next = m_head->m_next;
|
||||
}
|
||||
|
||||
m_dirty = true;
|
||||
|
||||
return iterator(*this, n);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
@@ -1759,6 +1794,8 @@ void category::swap_item(uint16_t item_ix, row_handle &a, row_handle &b)
|
||||
auto &ra = *a.m_row;
|
||||
auto &rb = *b.m_row;
|
||||
|
||||
m_dirty = true;
|
||||
|
||||
while (ra.size() <= item_ix)
|
||||
ra.emplace_back("");
|
||||
|
||||
@@ -1773,6 +1810,8 @@ void category::sort(std::function<int(row_handle, row_handle)> f)
|
||||
if (m_head == nullptr)
|
||||
return;
|
||||
|
||||
m_dirty = true;
|
||||
|
||||
std::vector<row_handle> rows;
|
||||
for (auto itemRow = m_head; itemRow != nullptr; itemRow = itemRow->m_next)
|
||||
rows.emplace_back(*this, *itemRow);
|
||||
@@ -1912,10 +1951,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)
|
||||
@@ -1936,10 +1975,43 @@ 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, true);
|
||||
break;
|
||||
|
||||
case output_format::tsv:
|
||||
write_delimited(os, order, addMissingItems, "\t", false, true);
|
||||
break;
|
||||
|
||||
case output_format::list:
|
||||
write_delimited(os, order, addMissingItems, "|", false, false);
|
||||
break;
|
||||
|
||||
case output_format::column:
|
||||
write_delimited(os, order, addMissingItems, " ", true, true);
|
||||
break;
|
||||
|
||||
case output_format::markdown:
|
||||
write_markdown(os, order, addMissingItems);
|
||||
break;
|
||||
|
||||
case output_format::table:
|
||||
write_table(os, order, addMissingItems, true);
|
||||
break;
|
||||
|
||||
case output_format::box:
|
||||
write_table(os, order, addMissingItems, false);
|
||||
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;
|
||||
@@ -2112,6 +2184,423 @@ 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, bool header) const
|
||||
{
|
||||
if (empty())
|
||||
return;
|
||||
|
||||
std::vector<bool> right_aligned(m_items.size(), false);
|
||||
|
||||
if (aligned and 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);
|
||||
return std::string{ s };
|
||||
}
|
||||
};
|
||||
|
||||
if (aligned)
|
||||
{
|
||||
if (header)
|
||||
{
|
||||
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 (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 (delimiter == " ")
|
||||
{
|
||||
int l = (w - s.length()) / 2;
|
||||
int r = w - s.length() - l;
|
||||
os << std::string(l, ' ') << s << std::string(r, ' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
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';
|
||||
|
||||
if (delimiter == " ")
|
||||
{
|
||||
for (bool first = true; uint16_t cix : order)
|
||||
{
|
||||
if (not std::exchange(first, false))
|
||||
os << delimiter;
|
||||
os << std::string(itemWidths[cix], '-');
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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());
|
||||
|
||||
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 = v->text().length();
|
||||
if (itemWidths[ix] < l)
|
||||
itemWidths[ix] = l;
|
||||
}
|
||||
}
|
||||
|
||||
os << "| ";
|
||||
for (bool first = true; uint16_t cix : order)
|
||||
{
|
||||
if (not std::exchange(first, false))
|
||||
os << " | ";
|
||||
|
||||
std::size_t w = itemWidths[cix];
|
||||
std::string_view s = m_items[cix].m_name;
|
||||
|
||||
if (s.length() < w)
|
||||
{
|
||||
int l = (w - s.length()) / 2;
|
||||
int r = w - s.length() - l;
|
||||
os << std::string(l, ' ') << s << std::string(r, ' ');
|
||||
}
|
||||
else
|
||||
os << s;
|
||||
}
|
||||
os << " |\n";
|
||||
|
||||
os << "| ";
|
||||
for (bool first = true; uint16_t cix : order)
|
||||
{
|
||||
if (not std::exchange(first, false))
|
||||
os << " | ";
|
||||
if (not right_aligned[cix])
|
||||
os << ':';
|
||||
os << std::string(itemWidths[cix] - 1, '-');
|
||||
if (right_aligned[cix])
|
||||
os << ':';
|
||||
}
|
||||
os << " |\n";
|
||||
|
||||
for (auto r = m_head; r != nullptr; r = r->m_next) // loop over rows
|
||||
{
|
||||
os << "| ";
|
||||
for (bool first = true; uint16_t cix : order)
|
||||
{
|
||||
if (not std::exchange(first, false))
|
||||
os << " | ";
|
||||
|
||||
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_table(std::ostream &os, const std::vector<uint16_t> &order, bool includeEmptyItems, bool ascii) const
|
||||
{
|
||||
static constexpr const std::string_view
|
||||
kUnicodeBox[13] = {
|
||||
"┌─", "─┬─", "─┐\n",
|
||||
"├─", "─┼─", "─┤\n",
|
||||
"└─", "─┴─", "─┘\n",
|
||||
"│ ", " │ ", " │\n",
|
||||
"─"
|
||||
},
|
||||
kAsciiBox[13] = { //
|
||||
"+-", "-+-", "-+\n", //
|
||||
"+-", "-+-", "-+\n", //
|
||||
"+-", "-+-", "-+\n", //
|
||||
"| ", " | ", " |\n", //
|
||||
"-"
|
||||
};
|
||||
|
||||
if (empty())
|
||||
return;
|
||||
|
||||
auto box = ascii ? kAsciiBox : kUnicodeBox;
|
||||
|
||||
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());
|
||||
|
||||
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 = v->text().length();
|
||||
if (itemWidths[ix] < l)
|
||||
itemWidths[ix] = l;
|
||||
}
|
||||
}
|
||||
|
||||
os << box[0];
|
||||
for (bool first = true; uint16_t cix : order)
|
||||
{
|
||||
if (not std::exchange(first, false))
|
||||
os << box[1];
|
||||
for (size_t i = 0; i < itemWidths[cix]; ++i)
|
||||
os << box[12];
|
||||
}
|
||||
os << box[2];
|
||||
|
||||
os << box[9];
|
||||
for (bool first = true; uint16_t cix : order)
|
||||
{
|
||||
if (not std::exchange(first, false))
|
||||
os << box[10];
|
||||
|
||||
std::size_t w = itemWidths[cix];
|
||||
std::string_view s = m_items[cix].m_name;
|
||||
|
||||
if (s.length() < w)
|
||||
{
|
||||
int l = (w - s.length()) / 2;
|
||||
int r = w - s.length() - l;
|
||||
os << std::string(l, ' ') << s << std::string(r, ' ');
|
||||
}
|
||||
else
|
||||
os << s;
|
||||
}
|
||||
os << box[11];
|
||||
|
||||
os << box[3];
|
||||
for (bool first = true; uint16_t cix : order)
|
||||
{
|
||||
if (not std::exchange(first, false))
|
||||
os << box[4];
|
||||
for (size_t i = 0; i < itemWidths[cix]; ++i)
|
||||
os << box[12];
|
||||
}
|
||||
os << box[5];
|
||||
|
||||
for (auto r = m_head; r != nullptr; r = r->m_next) // loop over rows
|
||||
{
|
||||
os << box[9];
|
||||
for (bool first = true; uint16_t cix : order)
|
||||
{
|
||||
if (not std::exchange(first, false))
|
||||
os << box[10];
|
||||
|
||||
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 << box[11];
|
||||
}
|
||||
|
||||
os << box[6];
|
||||
for (bool first = true; uint16_t cix : order)
|
||||
{
|
||||
if (not std::exchange(first, false))
|
||||
os << box[7];
|
||||
for (size_t i = 0; i < itemWidths[cix]; ++i)
|
||||
os << box[12];
|
||||
}
|
||||
os << box[8];
|
||||
}
|
||||
|
||||
bool category::operator==(const category &rhs) const
|
||||
{
|
||||
// shortcut
|
||||
|
||||
1102
src/cql.cpp
Normal file
1102
src/cql.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -48,7 +48,7 @@ void datablock::load_dictionary()
|
||||
{
|
||||
try
|
||||
{
|
||||
set_validator(&validator_factory::instance().get(*audit_conform));
|
||||
set_validator(validator_factory::instance().get(*audit_conform));
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
@@ -57,6 +57,18 @@ void datablock::load_dictionary()
|
||||
}
|
||||
}
|
||||
|
||||
void datablock::load_dictionary(std::string_view dict)
|
||||
{
|
||||
try
|
||||
{
|
||||
set_validator(validator_factory::instance().get(dict));
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
std::clog << ex.what() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void datablock::set_validator(const validator *v)
|
||||
{
|
||||
m_validator = v;
|
||||
|
||||
@@ -353,6 +353,7 @@ sac_parser::CIFToken sac_parser::get_next_token()
|
||||
{
|
||||
retract();
|
||||
result = CIFToken::VALUE;
|
||||
m_token_value = "?";
|
||||
}
|
||||
else
|
||||
state = State::Value;
|
||||
|
||||
@@ -26,21 +26,17 @@
|
||||
|
||||
#include "pdb2cif_remark_3.hpp"
|
||||
|
||||
#include "cif++.hpp"
|
||||
#include <cif++/compound.hpp>
|
||||
#include <cif++/gzio.hpp>
|
||||
#include <cif++/model.hpp>
|
||||
#include <cif++/pdb.hpp>
|
||||
#include <cif++/symmetry.hpp>
|
||||
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <stdexcept>
|
||||
|
||||
using cif::category;
|
||||
using cif::datablock;
|
||||
using cif::iequals;
|
||||
using cif::key;
|
||||
using cif::to_lower;
|
||||
using cif::to_lower_copy;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// attempt to come up with better error handling
|
||||
|
||||
@@ -6374,7 +6370,7 @@ void read_pdb_file(std::istream &pdbFile, cif::file &cifFile)
|
||||
{
|
||||
cifFile.front().load_dictionary();
|
||||
if (cifFile.front().get_validator() == nullptr)
|
||||
cifFile.front().set_validator(&validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
cifFile.front().set_validator(validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
|
||||
if (not cifFile.is_valid() and cif::VERBOSE >= 0)
|
||||
std::cerr << "Resulting mmCIF file is not valid!\n";
|
||||
@@ -6444,7 +6440,7 @@ file read(std::istream &is)
|
||||
{
|
||||
auto &db = result.front();
|
||||
if (db.get_validator() == nullptr)
|
||||
db.set_validator(&validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
db.set_validator(validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
if (db.is_valid())
|
||||
db.get_validator()->fill_audit_conform(db["audit_conform"]);
|
||||
}
|
||||
|
||||
@@ -1478,7 +1478,7 @@ bool Remark3Parser::parse(const std::string &expMethod, PDBRecord *r, cif::datab
|
||||
|
||||
best.parser->fixup();
|
||||
|
||||
auto &validator = cif::validator_factory::instance().get("mmcif_pdbx.dic");
|
||||
auto &validator = cif::validator_factory::instance()["mmcif_pdbx.dic"];
|
||||
|
||||
for (auto &cat1 : best.parser->mDb)
|
||||
{
|
||||
|
||||
@@ -1464,9 +1464,9 @@ bool reconstruct_pdbx(file &file)
|
||||
auto &db = file.front();
|
||||
|
||||
if (auto ac = db.get("audit_conform"); ac != nullptr)
|
||||
return reconstruct_pdbx(file, validator_factory::instance().get(*ac));
|
||||
return reconstruct_pdbx(file, validator_factory::instance()[*ac]);
|
||||
else
|
||||
return reconstruct_pdbx(file, validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
return reconstruct_pdbx(file, validator_factory::instance()["mmcif_pdbx.dic"]);
|
||||
}
|
||||
|
||||
bool reconstruct_pdbx(file &file, const validator &validator)
|
||||
@@ -1689,9 +1689,9 @@ void fixup_pdbx(file &file)
|
||||
auto &db = file.front();
|
||||
|
||||
if (auto ac = db.get("audit_conform"); ac != nullptr)
|
||||
fixup_pdbx(file, validator_factory::instance().get(*ac));
|
||||
fixup_pdbx(file, validator_factory::instance()[*ac]);
|
||||
else
|
||||
fixup_pdbx(file, validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
fixup_pdbx(file, validator_factory::instance()["mmcif_pdbx.dic"]);
|
||||
}
|
||||
|
||||
void fixup_pdbx(file &file, const validator &validator)
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
|
||||
#include "cif++.hpp"
|
||||
#include "cif++/validate.hpp"
|
||||
|
||||
namespace cif::pdb
|
||||
{
|
||||
@@ -65,6 +66,13 @@ condition get_parents_condition(const validator &validator, row_handle rh, const
|
||||
return result;
|
||||
}
|
||||
|
||||
bool is_valid_pdbx_file(const file &file)
|
||||
{
|
||||
std::error_code ec;
|
||||
bool result = is_valid_pdbx_file(file, validator_factory::instance()["mmcif_pdbx.dic"], ec);
|
||||
return result and not(bool) ec;
|
||||
}
|
||||
|
||||
bool is_valid_pdbx_file(const file &file, const validator &v)
|
||||
{
|
||||
std::error_code ec;
|
||||
@@ -79,9 +87,9 @@ bool is_valid_pdbx_file(const file &file, std::error_code &ec)
|
||||
if (file.empty())
|
||||
ec = make_error_code(validation_error::empty_file);
|
||||
else if (auto ac = file.front().get("audit_conform"); ac != nullptr)
|
||||
result = is_valid_pdbx_file(file, validator_factory::instance().get(*ac), ec);
|
||||
result = is_valid_pdbx_file(file, validator_factory::instance()[*ac], ec);
|
||||
else
|
||||
result = is_valid_pdbx_file(file, validator_factory::instance().get("mmcif_pdbx.dic"), ec);
|
||||
result = is_valid_pdbx_file(file, validator_factory::instance()["mmcif_pdbx.dic"], ec);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
265928
src/sqlite3/sqlite3.c
Normal file
265928
src/sqlite3/sqlite3.c
Normal file
File diff suppressed because it is too large
Load Diff
13972
src/sqlite3/sqlite3.h
Normal file
13972
src/sqlite3/sqlite3.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -25,13 +25,17 @@
|
||||
*/
|
||||
|
||||
#include "cif++/validate.hpp"
|
||||
|
||||
#include "cif++/category.hpp"
|
||||
#include "cif++/dictionary_parser.hpp"
|
||||
#include "cif++/utilities.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <format>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
|
||||
// The validator depends on regular expressions. Unfortunately,
|
||||
// the implementation of std::regex in g++ is buggy and crashes
|
||||
@@ -267,7 +271,7 @@ void item_validator::operator()(std::string_view value) const
|
||||
{
|
||||
std::error_code ec;
|
||||
if (not validate_value(value, ec))
|
||||
throw std::system_error(ec, std::string{ value } + " does not match rx for " + m_item_name);
|
||||
throw std::system_error(ec, std::format("'{}' is not a valid value for {}", value, m_item_name));
|
||||
}
|
||||
|
||||
bool item_validator::validate_value(std::string_view value, std::error_code &ec) const noexcept
|
||||
@@ -294,15 +298,20 @@ void category_validator::add_item_validator(item_validator &&v)
|
||||
|
||||
v.m_category = m_name;
|
||||
|
||||
auto r = m_item_validators.insert(std::move(v));
|
||||
if (not r.second and VERBOSE >= 4)
|
||||
std::cout << "Could not add validator for item " << v.m_item_name << " to category " << m_name << '\n';
|
||||
auto i = std::find(m_item_validators.begin(), m_item_validators.end(), v);
|
||||
if (i != m_item_validators.end())
|
||||
{
|
||||
if (VERBOSE >= 4)
|
||||
std::cout << "Could not add validator for item " << v.m_item_name << " to category " << m_name << '\n';
|
||||
}
|
||||
else
|
||||
m_item_validators.emplace_back(std::move(v));
|
||||
}
|
||||
|
||||
const item_validator *category_validator::get_validator_for_item(std::string_view item_name) const
|
||||
{
|
||||
const item_validator *result = nullptr;
|
||||
auto i = m_item_validators.find(item_validator{ std::string(item_name) });
|
||||
auto i = std::find(m_item_validators.begin(), m_item_validators.end(), item_validator{ std::string(item_name) });
|
||||
if (i != m_item_validators.end())
|
||||
result = &*i;
|
||||
else if (VERBOSE > 4)
|
||||
@@ -539,19 +548,18 @@ validator_factory &validator_factory::instance()
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
const validator &validator_factory::get(std::string_view dictionary_name)
|
||||
const validator *validator_factory::get(std::string_view dictionary_name)
|
||||
{
|
||||
category audit_conform("audit_conform");
|
||||
for (auto part : cif::split(dictionary_name, ";", true))
|
||||
for (auto part : cif::split(dictionary_name, ";,", true))
|
||||
audit_conform.emplace({ { "dict_name", part } });
|
||||
|
||||
return get(audit_conform);
|
||||
}
|
||||
|
||||
const validator &validator_factory::get(const category &audit_conform)
|
||||
const validator *validator_factory::get(const category &audit_conform)
|
||||
{
|
||||
if (audit_conform.empty())
|
||||
throw std::runtime_error("Empty audit_conform category, cannot create a validator");
|
||||
const validator *result = nullptr;
|
||||
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
@@ -559,42 +567,60 @@ const validator &validator_factory::get(const category &audit_conform)
|
||||
for (auto &v : m_validators)
|
||||
{
|
||||
if (v.matches_audit_conform(audit_conform))
|
||||
return v;
|
||||
result = &v;
|
||||
}
|
||||
|
||||
// If the audit conform contains only one record, this is easy
|
||||
if (audit_conform.size() == 1)
|
||||
if (result == nullptr and audit_conform.size() == 1)
|
||||
{
|
||||
const auto &[name, version] =
|
||||
audit_conform.front().get<std::string, std::optional<std::string>>("dict_name", "dict_version");
|
||||
if (not name.empty())
|
||||
return m_validators.emplace_back(construct_validator(name, version));
|
||||
result = &m_validators.emplace_back(construct_validator(name, version));
|
||||
}
|
||||
|
||||
// A new, merged dictionary
|
||||
|
||||
std::optional<validator> v;
|
||||
for (const auto &[name, version] : audit_conform.rows<std::string, std::optional<std::string>>("dict_name", "dict_version"))
|
||||
if (result == nullptr)
|
||||
{
|
||||
if (name.empty())
|
||||
continue;
|
||||
|
||||
if (not v)
|
||||
v = construct_validator(name, version);
|
||||
else
|
||||
// A new, merged dictionary
|
||||
std::optional<validator> v;
|
||||
for (const auto &[name, version] : audit_conform.rows<std::string, std::optional<std::string>>("dict_name", "dict_version"))
|
||||
{
|
||||
auto data = load_resource(name);
|
||||
if (not data)
|
||||
throw std::runtime_error("Could not load dictionary " + std::string{ name });
|
||||
if (name.empty())
|
||||
continue;
|
||||
|
||||
v->parse(*data);
|
||||
if (not v) // first dict
|
||||
v = construct_validator(name, version);
|
||||
else // additional/extending dict
|
||||
{
|
||||
auto data = load_resource(name);
|
||||
if (not data)
|
||||
throw std::runtime_error("Could not load dictionary " + std::string{ name });
|
||||
|
||||
v->parse(*data);
|
||||
}
|
||||
}
|
||||
|
||||
if (v)
|
||||
result = &m_validators.emplace_back(std::move(*v));
|
||||
}
|
||||
|
||||
if (not v)
|
||||
throw std::runtime_error("Missing dictionary information?");
|
||||
return result;
|
||||
}
|
||||
|
||||
return m_validators.emplace_back(std::move(*v));
|
||||
const validator &validator_factory::operator[](const category &audit_conform)
|
||||
{
|
||||
auto v = get(audit_conform);
|
||||
if (v == nullptr)
|
||||
throw std::runtime_error("Could not load dictionary for audit_conform");
|
||||
return *v;
|
||||
}
|
||||
|
||||
const validator &validator_factory::operator[](std::string_view dictionary_name)
|
||||
{
|
||||
auto v = get(dictionary_name);
|
||||
if (v == nullptr)
|
||||
throw std::runtime_error("Could not load dictionary for " + std::string{ dictionary_name });
|
||||
return *v;
|
||||
}
|
||||
|
||||
validator validator_factory::construct_validator(std::string_view name, std::optional<std::string> version)
|
||||
|
||||
@@ -49,6 +49,7 @@ list(
|
||||
spinner
|
||||
reconstruction
|
||||
validate-pdbx
|
||||
cql
|
||||
matrix
|
||||
)
|
||||
|
||||
|
||||
537
test/cql-test.cpp
Normal file
537
test/cql-test.cpp
Normal file
@@ -0,0 +1,537 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2025 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
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "test-main.hpp"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <cif++.hpp>
|
||||
#include <cif++/cql.hpp>
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
cif::file operator""_cf(const char *text, std::size_t length)
|
||||
{
|
||||
struct membuf : public std::streambuf
|
||||
{
|
||||
membuf(char *text, std::size_t length)
|
||||
{
|
||||
this->setg(text, text, text + length);
|
||||
}
|
||||
} buffer(const_cast<char *>(text), length);
|
||||
|
||||
std::istream is(&buffer);
|
||||
return cif::file(is);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
const char *kAuthors[] = {
|
||||
"Kleywegt, G.J.",
|
||||
"Bergfors, T.",
|
||||
"Senn, H.",
|
||||
"Le Motte, P.",
|
||||
"Gsell, B.",
|
||||
"Shudo, K.",
|
||||
"Jones, T.A.",
|
||||
|
||||
"Banaszak, L.",
|
||||
"Winter, N.",
|
||||
"Xu, Z.",
|
||||
"Bernlohr, D.A.",
|
||||
"Cowan, S.W.",
|
||||
"Jones, T.A.",
|
||||
"Bergfors, T.",
|
||||
"Kleywegt, G.J.",
|
||||
"Jones, T.A.",
|
||||
"Cowan, S.W.",
|
||||
"Newcomer, M.E.",
|
||||
"Jones, T.A.",
|
||||
"Jones, T.A.",
|
||||
"Bergfors, T.",
|
||||
"Sedzik, J.",
|
||||
"Unge, T."
|
||||
};
|
||||
|
||||
// Test simple SELECT
|
||||
TEST_CASE("cql-1")
|
||||
{
|
||||
cif::file f(gTestDir / ".." / "examples" / "1cbs.cif.gz");
|
||||
auto &db = f.front();
|
||||
db.load_dictionary("mmcif_pdbx.dic");
|
||||
|
||||
cif::cql::connection connection(db);
|
||||
cif::cql::transaction tx(connection);
|
||||
|
||||
auto r = tx.exec("SELECT name, ordinal FROM citation_author WHERE citation_id = 'primary';");
|
||||
CHECK(r.size() == 7);
|
||||
|
||||
for (size_t ix = 0; auto row : r)
|
||||
{
|
||||
REQUIRE(ix < (sizeof(kAuthors) / sizeof(char *)));
|
||||
|
||||
CHECK(row[0].as<std::string>() == kAuthors[ix]);
|
||||
CHECK(row[1].as<size_t>() == ix + 1);
|
||||
|
||||
CHECK(row["name"].as<std::string>() == kAuthors[ix]);
|
||||
CHECK(row["ordinal"].as<size_t>() == ix + 1);
|
||||
|
||||
++ix;
|
||||
}
|
||||
|
||||
r = tx.exec("SELECT ordinal, name FROM citation_author WHERE citation_id = 'primary';");
|
||||
CHECK(r.size() == 7);
|
||||
|
||||
for (size_t ix = 0; auto row : r)
|
||||
{
|
||||
REQUIRE(ix < (sizeof(kAuthors) / sizeof(char *)));
|
||||
|
||||
CHECK(row[1].as<std::string>() == kAuthors[ix]);
|
||||
CHECK(row[0].as<size_t>() == ix + 1);
|
||||
|
||||
CHECK(row["name"].as<std::string>() == kAuthors[ix]);
|
||||
CHECK(row["ordinal"].as<size_t>() == ix + 1);
|
||||
|
||||
++ix;
|
||||
}
|
||||
|
||||
r = tx.exec("SELECT * FROM citation_author WHERE citation_id = 'primary';");
|
||||
CHECK(r.size() == 7);
|
||||
|
||||
for (int ix = 0; auto row : r)
|
||||
{
|
||||
REQUIRE(static_cast<size_t>(ix) < (sizeof(kAuthors) / sizeof(char *)));
|
||||
|
||||
for (auto fld : row)
|
||||
{
|
||||
switch (fld.num())
|
||||
{
|
||||
case 0:
|
||||
CHECK(fld.name() == "citation_id");
|
||||
CHECK(fld.as<std::string>() == "primary");
|
||||
break;
|
||||
case 1:
|
||||
CHECK(fld.name() == "name");
|
||||
CHECK(fld.as<std::string>() == kAuthors[ix]);
|
||||
break;
|
||||
case 2:
|
||||
CHECK(fld.name() == "ordinal");
|
||||
CHECK(fld.as<int>() == ix + 1);
|
||||
break;
|
||||
default:
|
||||
REQUIRE(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CHECK(row["name"].as<std::string>() == kAuthors[ix]);
|
||||
CHECK(row["ordinal"].as<int>() == ix + 1);
|
||||
CHECK(row["citation_id"].as<std::string>() == "primary");
|
||||
|
||||
++ix;
|
||||
}
|
||||
}
|
||||
|
||||
// Test SELECT AS
|
||||
TEST_CASE("cql-2")
|
||||
{
|
||||
cif::file f(gTestDir / ".." / "examples" / "1cbs.cif.gz");
|
||||
auto &db = f.front();
|
||||
db.load_dictionary("mmcif_pdbx.dic");
|
||||
|
||||
cif::cql::connection connection(db);
|
||||
cif::cql::transaction tx(connection);
|
||||
|
||||
auto r = tx.exec("SELECT name AS v1, ordinal AS v2 FROM citation_author WHERE citation_id = 'primary';");
|
||||
CHECK(r.size() == 7);
|
||||
|
||||
for (size_t ix = 0; auto row : r)
|
||||
{
|
||||
REQUIRE(ix < (sizeof(kAuthors) / sizeof(char *)));
|
||||
|
||||
CHECK(row[0].as<std::string>() == kAuthors[ix]);
|
||||
CHECK(row[1].as<size_t>() == ix + 1);
|
||||
|
||||
CHECK(row["v1"].as<std::string>() == kAuthors[ix]);
|
||||
CHECK(row["v2"].as<size_t>() == ix + 1);
|
||||
|
||||
++ix;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("cql-3")
|
||||
{
|
||||
cif::file f(gTestDir / ".." / "examples" / "1cbs.cif.gz");
|
||||
auto &db = f.front();
|
||||
db.load_dictionary("mmcif_pdbx.dic");
|
||||
|
||||
cif::cql::connection connection(db);
|
||||
cif::cql::transaction tx(connection);
|
||||
|
||||
auto r = tx.exec("SELECT name FROM citation_author WHERE ordinal = 10").one_field();
|
||||
CHECK(r.as<std::string>() == kAuthors[9]);
|
||||
}
|
||||
|
||||
TEST_CASE("cql-4")
|
||||
{
|
||||
cif::file f(gTestDir / ".." / "examples" / "1cbs.cif.gz");
|
||||
auto &db = f.front();
|
||||
db.load_dictionary("mmcif_pdbx.dic");
|
||||
|
||||
cif::cql::connection connection(db);
|
||||
cif::cql::transaction tx(connection);
|
||||
|
||||
auto r = tx.exec("SELECT name FROM citation_author WHERE ordinal BETWEEN 10 AND 15");
|
||||
REQUIRE(r.size() == 6);
|
||||
}
|
||||
|
||||
TEST_CASE("cql-5")
|
||||
{
|
||||
cif::file f(gTestDir / ".." / "examples" / "1cbs.cif.gz");
|
||||
auto &db = f.front();
|
||||
db.load_dictionary("mmcif_pdbx.dic");
|
||||
|
||||
cif::cql::connection connection(db);
|
||||
cif::cql::transaction tx(connection);
|
||||
|
||||
auto r = tx.exec("SELECT (SELECT year FROM citation WHERE id = citation_id) AS jaar FROM citation_author WHERE ordinal IS 23").one_field();
|
||||
CHECK(r.name() == "jaar");
|
||||
CHECK(r.as<int>() == 1988);
|
||||
}
|
||||
|
||||
TEST_CASE("cql-6")
|
||||
{
|
||||
cif::file f(gTestDir / ".." / "examples" / "1cbs.cif.gz");
|
||||
auto &db = f.front();
|
||||
db.load_dictionary("mmcif_pdbx.dic");
|
||||
|
||||
cif::cql::connection connection(db);
|
||||
cif::cql::transaction tx(connection);
|
||||
|
||||
auto r = tx.exec("SELECT COUNT(*) FROM citation WHERE page_last IS NULL").one_field();
|
||||
CHECK(r.as<int>() == 4);
|
||||
|
||||
r = tx.exec("SELECT COUNT(*) FROM citation WHERE page_last IS NOT NULL").one_field();
|
||||
CHECK(r.as<int>() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("cql-stream-1")
|
||||
{
|
||||
cif::file f(gTestDir / ".." / "examples" / "1cbs.cif.gz");
|
||||
auto &db = f.front();
|
||||
db.load_dictionary("mmcif_pdbx.dic");
|
||||
|
||||
cif::cql::connection connection(db);
|
||||
cif::cql::transaction tx(connection);
|
||||
|
||||
for (size_t ix = 0;
|
||||
const auto &[name, ordinal] : tx.stream<std::string, size_t>(
|
||||
"SELECT name, ordinal FROM citation_author WHERE citation_id = 'primary';"))
|
||||
{
|
||||
REQUIRE(ix < (sizeof(kAuthors) / sizeof(char *)));
|
||||
|
||||
CHECK(name == kAuthors[ix]);
|
||||
CHECK(ordinal == (ix + 1));
|
||||
|
||||
++ix;
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("cql-insert-1")
|
||||
{
|
||||
auto f1 = R"(
|
||||
data_T1
|
||||
loop_
|
||||
_table1.id
|
||||
_table1.name
|
||||
1 aap
|
||||
2 noot)"_cf;
|
||||
auto f0 = f1;
|
||||
|
||||
auto &db = f1.front();
|
||||
|
||||
cif::cql::connection connection(db);
|
||||
cif::cql::transaction tx(connection);
|
||||
|
||||
auto count = tx.exec("SELECT COUNT(*) FROM table1;").one_field().as<int>();
|
||||
CHECK(count == 2);
|
||||
|
||||
auto r = tx.exec("INSERT INTO table1 (id, name) VALUES (3, 'mies')");
|
||||
|
||||
count = tx.exec("SELECT COUNT(*) FROM table1").one_field().as<int>();
|
||||
CHECK(count == 3);
|
||||
|
||||
(void)tx.exec("DELETE FROM table1 WHERE CAST(id AS INTEGER) = 1;");
|
||||
|
||||
count = tx.exec("SELECT COUNT(*) FROM table1;").one_field().as<int>();
|
||||
CHECK(count == 2);
|
||||
|
||||
(void)tx.exec("UPDATE table1 SET name = 'amandel' WHERE CAST(id AS INTEGER) = 2");
|
||||
|
||||
auto f2 = R"(
|
||||
data_T1
|
||||
loop_
|
||||
_table1.id
|
||||
_table1.name
|
||||
2 amandel
|
||||
3 mies)"_cf;
|
||||
|
||||
CHECK(f1 == f2);
|
||||
|
||||
tx.rollback();
|
||||
|
||||
CHECK(f1 == f0);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("cql-rename")
|
||||
{
|
||||
auto f1 = R"(
|
||||
data_T1
|
||||
loop_
|
||||
_table1.id
|
||||
_table1.name
|
||||
1 aap
|
||||
2 noot)"_cf;
|
||||
|
||||
auto &db = f1.front();
|
||||
|
||||
cif::cql::connection connection(db);
|
||||
cif::cql::transaction tx(connection);
|
||||
|
||||
(void)tx.exec("ALTER TABLE table1 RENAME TO 'table2'");
|
||||
|
||||
auto f2 = R"(
|
||||
data_T1
|
||||
loop_
|
||||
_table2.id
|
||||
_table2.name
|
||||
1 aap
|
||||
2 noot)"_cf;
|
||||
|
||||
CHECK(f1 == f2);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("cql-foreign-keys-1")
|
||||
{
|
||||
const char dict[] = R"(
|
||||
data_test_dict.dic
|
||||
_datablock.id test_dict.dic
|
||||
_datablock.description
|
||||
;
|
||||
A test dictionary
|
||||
;
|
||||
_dictionary.title test_dict.dic
|
||||
_dictionary.datablock_id test_dict.dic
|
||||
_dictionary.version 1.0
|
||||
|
||||
loop_
|
||||
_item_type_list.code
|
||||
_item_type_list.primitive_code
|
||||
_item_type_list.construct
|
||||
_item_type_list.detail
|
||||
code char
|
||||
'[][_,.;:"&<>()/\{}'`~!@#$%A-Za-z0-9*|+-]*'
|
||||
; code item types/single words ...
|
||||
;
|
||||
text char
|
||||
'[][ \n\t()_,.;:"&<>/\{}'`~!@#$%?+=*A-Za-z0-9|^-]*'
|
||||
; text item types / multi-line text ...
|
||||
;
|
||||
int numb
|
||||
'[+-]?[0-9]+'
|
||||
; int item types are the subset of numbers that are the negative
|
||||
or positive integers.
|
||||
;
|
||||
|
||||
save_cat_1
|
||||
_category.description 'A simple test category'
|
||||
_category.id cat_1
|
||||
_category.mandatory_code no
|
||||
_category_key.name '_cat_1.id'
|
||||
|
||||
save_
|
||||
|
||||
save__cat_1.id
|
||||
_item.name '_cat_1.id'
|
||||
_item.category_id cat_1
|
||||
_item.mandatory_code yes
|
||||
_item_aliases.dictionary cif_core.dic
|
||||
_item_aliases.version 2.0.1
|
||||
_item_linked.child_name '_cat_2.parent_id'
|
||||
_item_linked.parent_name '_cat_1.id'
|
||||
_item_type.code code
|
||||
save_
|
||||
|
||||
save__cat_1.name
|
||||
_item.name '_cat_1.name'
|
||||
_item.category_id cat_1
|
||||
_item.mandatory_code yes
|
||||
_item_aliases.dictionary cif_core.dic
|
||||
_item_aliases.version 2.0.1
|
||||
_item_type.code text
|
||||
save_
|
||||
|
||||
save_cat_2
|
||||
_category.description 'A second simple test category'
|
||||
_category.id cat_2
|
||||
_category.mandatory_code no
|
||||
_category_key.name '_cat_2.id'
|
||||
save_
|
||||
|
||||
save__cat_2.id
|
||||
_item.name '_cat_2.id'
|
||||
_item.category_id cat_2
|
||||
_item.mandatory_code yes
|
||||
_item_aliases.dictionary cif_core.dic
|
||||
_item_aliases.version 2.0.1
|
||||
_item_type.code int
|
||||
save_
|
||||
|
||||
save__cat_2.parent_id
|
||||
_item.name '_cat_2.parent_id'
|
||||
_item.category_id cat_2
|
||||
_item.mandatory_code yes
|
||||
_item_aliases.dictionary cif_core.dic
|
||||
_item_aliases.version 2.0.1
|
||||
_item_type.code code
|
||||
save_
|
||||
|
||||
save__cat_2.desc
|
||||
_item.name '_cat_2.desc'
|
||||
_item.category_id cat_2
|
||||
_item.mandatory_code yes
|
||||
_item_aliases.dictionary cif_core.dic
|
||||
_item_aliases.version 2.0.1
|
||||
_item_type.code text
|
||||
save_
|
||||
)";
|
||||
|
||||
struct membuf : public std::streambuf
|
||||
{
|
||||
membuf(char *text, std::size_t length)
|
||||
{
|
||||
this->setg(text, text, text + length);
|
||||
}
|
||||
} buffer(const_cast<char *>(dict), sizeof(dict) - 1);
|
||||
|
||||
std::istream is_dict(&buffer);
|
||||
|
||||
cif::validator validator(is_dict);
|
||||
|
||||
cif::file f;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
const char data[] = R"(
|
||||
data_test
|
||||
loop_
|
||||
_cat_1.id
|
||||
_cat_1.name
|
||||
1 Aap
|
||||
2 Noot
|
||||
3 Mies
|
||||
|
||||
loop_
|
||||
_cat_2.id
|
||||
_cat_2.parent_id
|
||||
_cat_2.desc
|
||||
1 1 'Een dier'
|
||||
2 1 'Een andere aap'
|
||||
3 2 'walnoot bijvoorbeeld'
|
||||
)";
|
||||
|
||||
struct data_membuf : public std::streambuf
|
||||
{
|
||||
data_membuf(char *text, std::size_t length)
|
||||
{
|
||||
this->setg(text, text, text + length);
|
||||
}
|
||||
} data_buffer(const_cast<char *>(data), sizeof(data) - 1);
|
||||
|
||||
std::istream is_data(&data_buffer);
|
||||
f.load(is_data, validator);
|
||||
|
||||
auto &db = f.front();
|
||||
|
||||
cif::cql::connection connection(db);
|
||||
cif::cql::transaction tx(connection);
|
||||
|
||||
for (const auto &desc : tx.stream<std::string>(R"(SELECT b.desc FROM cat_1 a, cat_2 b WHERE a.id = b.parent_id AND a.name = 'Noot')"))
|
||||
{
|
||||
CHECK(desc == "walnoot bijvoorbeeld");
|
||||
}
|
||||
|
||||
// Check cascading delete
|
||||
tx.exec("DELETE FROM cat_1 WHERE id = 1");
|
||||
CHECK(db["cat_1"].size() == 2);
|
||||
CHECK(db["cat_2"].size() == 1);
|
||||
|
||||
tx.rollback();
|
||||
CHECK(db["cat_1"].size() == 3);
|
||||
CHECK(db["cat_2"].size() == 3);
|
||||
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("drop-table")
|
||||
{
|
||||
auto f1 = R"(
|
||||
data_T1
|
||||
loop_
|
||||
_table1.id
|
||||
_table1.name
|
||||
1 aap
|
||||
2 noot)"_cf;
|
||||
|
||||
auto &db = f1.front();
|
||||
|
||||
cif::cql::connection connection(db);
|
||||
cif::cql::transaction tx(connection);
|
||||
|
||||
SECTION("commit")
|
||||
{
|
||||
(void)tx.exec("DROP TABLE table1;");
|
||||
tx.commit();
|
||||
|
||||
CHECK(db.empty());
|
||||
}
|
||||
|
||||
// Ah, too bad: this doesn't work
|
||||
// SECTION("rollback")
|
||||
// {
|
||||
// (void)tx.exec("DROP TABLE table1;");
|
||||
// tx.rollback();
|
||||
|
||||
// CHECK(not db.empty());
|
||||
// CHECK(db["table1"].size() == 2);
|
||||
// }
|
||||
}
|
||||
@@ -54,7 +54,7 @@ TEST_CASE("create_nonpoly_1")
|
||||
|
||||
cif::file file;
|
||||
auto &&[dbi, ignore] = file.emplace("TEST"); // create a datablock
|
||||
dbi->set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
dbi->load_dictionary("mmcif_pdbx.dic");
|
||||
|
||||
cif::mm::structure structure(file);
|
||||
|
||||
@@ -82,7 +82,7 @@ _atom_site.pdbx_formal_charge
|
||||
# that's enough to test with
|
||||
)"_cf;
|
||||
|
||||
atoms.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
atoms.front().load_dictionary("mmcif_pdbx.dic");
|
||||
|
||||
auto &hem_data = atoms["HEM"];
|
||||
auto &atom_site = hem_data["atom_site"];
|
||||
@@ -159,7 +159,7 @@ _struct_asym.details ?
|
||||
_atom_type.symbol C
|
||||
)"_cf;
|
||||
|
||||
expected.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
expected.front().load_dictionary("mmcif_pdbx.dic");
|
||||
|
||||
if (not(expected.front() == structure.get_datablock()))
|
||||
{
|
||||
@@ -178,7 +178,7 @@ TEST_CASE("create_nonpoly_2")
|
||||
|
||||
cif::file file;
|
||||
auto &&[dbi, ignore] = file.emplace("TEST"); // create a datablock
|
||||
dbi->set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
dbi->load_dictionary("mmcif_pdbx.dic");
|
||||
|
||||
cif::mm::structure structure(file);
|
||||
|
||||
@@ -270,7 +270,7 @@ _struct_asym.details ?
|
||||
_atom_type.symbol C
|
||||
)"_cf;
|
||||
|
||||
expected.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
expected.front().load_dictionary("mmcif_pdbx.dic");
|
||||
|
||||
REQUIRE(expected.front() == structure.get_datablock());
|
||||
|
||||
@@ -354,7 +354,7 @@ _struct_asym.details ?
|
||||
#
|
||||
)"_cf;
|
||||
|
||||
data.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
data.front().load_dictionary("mmcif_pdbx.dic");
|
||||
|
||||
cif::mm::structure s(data);
|
||||
|
||||
@@ -566,7 +566,7 @@ _struct_asym.details ?
|
||||
#
|
||||
)"_cf;
|
||||
|
||||
data.front().set_validator(&cif::validator_factory::instance().get("mmcif_pdbx.dic"));
|
||||
data.front().load_dictionary("mmcif_pdbx.dic");
|
||||
|
||||
SECTION("max")
|
||||
{
|
||||
|
||||
@@ -278,13 +278,13 @@ TEST_CASE("item_2")
|
||||
CHECK(i0.value() == ".");
|
||||
|
||||
cif::item i1("test1", std:: optional<float>());
|
||||
CHECK(i1.value() == "?");
|
||||
CHECK(i1.is_unknown());
|
||||
|
||||
cif::item i2("test1", std::make_optional<float>(1.f));
|
||||
CHECK(i2.value() == "1");
|
||||
|
||||
cif::item i3("test1", std::optional<float>(), 2);
|
||||
CHECK(i3.value() == "?");
|
||||
CHECK(i3.is_unknown());
|
||||
|
||||
cif::item i4("test1", std::make_optional<float>(1.f), 2);
|
||||
CHECK(i4.value() == "1.00");
|
||||
@@ -2038,7 +2038,7 @@ TEST_CASE("r1")
|
||||
/*
|
||||
Rationale:
|
||||
|
||||
The pdbx_mmcif dictionary contains inconsistent child-parent relations. E.g. atom_site is parent
|
||||
The mmcif_pdbx.dic dictionary contains inconsistent child-parent relations. E.g. atom_site is parent
|
||||
of pdbx_nonpoly_scheme which itself is a parent of pdbx_entity_nonpoly. If I want to rename a residue
|
||||
I cannot update pdbx_nonpoly_scheme since changing a parent changes children, but not vice versa.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user