From cac4b904a6032afbbab561231ac7b5479e36a981 Mon Sep 17 00:00:00 2001 From: singularitti Date: Mon, 5 Sep 2022 15:56:52 -0400 Subject: [PATCH] Add all files --- README.md | 2 - src/CifAPI.jl | 3 + src/api.jl | 136 ++++++++++++++++++++++++++++++++++++++++ src/ctypes.jl | 15 +++++ src/handlers.jl | 161 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 315 insertions(+), 2 deletions(-) create mode 100644 src/api.jl create mode 100644 src/ctypes.jl create mode 100644 src/handlers.jl diff --git a/README.md b/README.md index 98642a7..0389295 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,6 @@ [![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://MineralsCloud.github.io/CifAPI.jl/stable/) [![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://MineralsCloud.github.io/CifAPI.jl/dev/) [![Build Status](https://github.com/MineralsCloud/CifAPI.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/MineralsCloud/CifAPI.jl/actions/workflows/CI.yml?query=branch%3Amain) -[![Build Status](https://github.com/MineralsCloud/CifAPI.jl/badges/main/pipeline.svg)](https://github.com/MineralsCloud/CifAPI.jl/pipelines) -[![Coverage](https://github.com/MineralsCloud/CifAPI.jl/badges/main/coverage.svg)](https://github.com/MineralsCloud/CifAPI.jl/commits/main) [![Build Status](https://ci.appveyor.com/api/projects/status/github/MineralsCloud/CifAPI.jl?svg=true)](https://ci.appveyor.com/project/MineralsCloud/CifAPI-jl) [![Build Status](https://api.cirrus-ci.com/github/MineralsCloud/CifAPI.jl.svg)](https://cirrus-ci.com/github/MineralsCloud/CifAPI.jl) [![Coverage](https://codecov.io/gh/MineralsCloud/CifAPI.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/MineralsCloud/CifAPI.jl) diff --git a/src/CifAPI.jl b/src/CifAPI.jl index 7b63c11..7ee32e8 100644 --- a/src/CifAPI.jl +++ b/src/CifAPI.jl @@ -1,6 +1,9 @@ module CifAPI include("wrappers.jl") +include("ctypes.jl") include("errors.jl") +include("handlers.jl") +include("api.jl") end diff --git a/src/api.jl b/src/api.jl new file mode 100644 index 0000000..78beefa --- /dev/null +++ b/src/api.jl @@ -0,0 +1,136 @@ +abstract type CifContainer <: AbstractDict{String} end +abstract type NestedCifContainer <: CifContainer end +mutable struct Block <: CifContainer + loop_names::Vector{Vector{String}} #one loop is a list of datanames + data_values::Dict{String,<:Vector} + original_file::AbstractPath +end +mutable struct CifBlock{V} <: NestedCifContainer{V} + save_frames::Dict{String,Block{V}} + loop_names::Vector{Vector{String}} #one loop is a list of datanames + data_values::Dict{String,Vector{V}} + original_file::AbstractPath +end + +get_data_values(b::Block) = b.data_values +get_data_values(b::CifBlock) = b.data_values + +abstract type CifCollection <: AbstractDict{String} end +struct Cif{T<:CifContainer} <: CifCollection + header_comments::String + contents::Dict{String,T} +end + +mutable struct Uchar + string::Ptr{UInt16} +end + +function get_block_code(b) + s = Uchar(0) + val = cif_container_get_code(b, Ref(s)) + checkerror(val) + return make_jl_string(s) +end + +function get_syntactical_type(t::cif_value_tp_ptr) + val_type = cif_value_kind(t.handle) + if val_type == 0 || val_type == 1 + return typeof(t) + elseif val_type == 2 + cif_list + elseif val_type == 3 + cif_table + elseif val_type == 4 + return Nothing + elseif val_type == 5 + return Missing + end +end + +function cif_list(cv::cif_value_tp_ptr) + cif_type = cif_value_kind(cv.handle) + if cif_type != 2 + error("$val is not a cif list value") + end + elctptr = Ref{Cint}(0) + val = cif_value_get_element_count(cv.handle, elctptr) + if val != 0 + error(ERROR_CODES[val]) + end + elct = elctptr[] + so_far = Vector() + for el_num in 1:elct + new_element = cif_value_tp_ptr(0) + val = cif_value_get_element_at(cv.handle, el_num - 1, Ref(new_element)) + if val != 0 + error(ERROR_CODES[val]) + end + t = get_syntactical_type(new_element) + if t == cif_value_tp_ptr + push!(so_far, String(new_element)) + elseif t == cif_list + push!(so_far, cif_list(new_element)) + elseif t == cif_table + push!(so_far, cif_table(new_element)) + else + push!(so_far, t()) + end + end + return so_far +end + +function cif_table(cv::cif_value_tp_ptr) + cif_type = cif_value_kind(cv.handle) + if cif_type != 3 + error("$val is not a cif table value") + end + so_far = Dict{String,Any}() + for el in keys(cv) + new_val = cv[el] + t = get_syntactical_type(new_val) + if t == cif_value_tp_ptr + so_far[el] = String(new_val) + elseif t == cif_list + so_far[el] = cif_list(new_val) + elseif t == cif_table + so_far[el] = cif_table(new_val) + else + so_far[el] = t() + end + end + return so_far +end + +function Base.String(t::cif_value_tp) + #Get the textual representation + s = Uchar(0) + val = cif_value_get_text(t.handle, Ref(s)) + checkerror(val) + return make_jl_string(s) +end + +function destroy!(cif) + val = cif_destroy(cif.data) + if val != 0 + error(ERROR_CODES[val]) + end + return +end + +function make_jl_string(s::Uchar) + n = get_c_length(s.string, -1) # short for testing + icu_string = unsafe_wrap(Array{UInt16,1}, s.string, n, own=false) + return transcode(String, icu_string) +end + +function get_c_length(s::Ptr, max=-1) + # Now loop over the values we have + n = 1 + b = unsafe_load(s, n) + while b != 0 && (max == -1 || (max != -1 && n < max)) + n = n + 1 + b = unsafe_load(s, n) + end + n = n - 1 + return n +end diff --git a/src/ctypes.jl b/src/ctypes.jl new file mode 100644 index 0000000..e7ad7b0 --- /dev/null +++ b/src/ctypes.jl @@ -0,0 +1,15 @@ +mutable struct cif_tp_ptr + handle::Ptr{cif_tp} +end + +mutable struct cif_container_tp_ptr + handle::Ptr{cif_container_tp} # *cif_block_tp +end + +mutable struct cif_loop_tp_ptr + handle::Ptr{cif_loop_tp} +end + +mutable struct cif_value_tp_ptr + handle::Ptr{cif_value_tp} +end diff --git a/src/handlers.jl b/src/handlers.jl new file mode 100644 index 0000000..6665401 --- /dev/null +++ b/src/handlers.jl @@ -0,0 +1,161 @@ +mutable struct cif_builder_context + actual_cif::Dict{String,<:CifContainer} + block_stack::Array{<:CifContainer} + filename::AbstractPath + verbose::Bool +end + +handle_cif_start(a, b)::Cint = 0 + +handle_cif_end(a, b)::Cint = 0 + +handle_block_start(a::cif_container_tp_ptr, b)::Cint = begin + blockname = get_block_code(a) + if b.verbose + println("New blockname $(blockname)") + end + newblock = Block() + newblock.original_file = b.filename + push!(b.block_stack, newblock) + return 0 +end + +function handle_block_end(a::cif_container_tp_ptr, b::cif_builder_context)::Cint + # Remove missing values + all_names = keys(get_data_values(b.block_stack[end])) + # Length > 1 dealt with already + all_names = filter(x -> length(get_data_values(b.block_stack[end])[x]) == 1, all_names) + # Remove any whose first and only entry is 'missing' + drop_names = filter(x -> ismissing(get_data_values(b.block_stack[end])[x][1]), all_names) + # println("Removing $drop_names from block") + [delete!(b.block_stack[end], x) for x in drop_names] + # and finish off + blockname = get_block_code(a) + if b.verbose + println("Block is finished: $blockname") + end + b.actual_cif[blockname] = pop!(b.block_stack) + return 0 +end + +function handle_frame_start(a::cif_container_tp_ptr, b)::Cint + blockname = get_block_code(a) + if b.verbose + println("Frame started: $blockname") + end + newblock = Block() + newblock.original_file = b.filename + b.block_stack[end] = CifBlock(b.block_stack[end]) + push!(b.block_stack, newblock) + return 0 +end + +function handle_frame_end(a, b)::Cint + # Remove missing values + all_names = keys(get_data_values(b.block_stack[end])) + # Length > 1 dealt with already + all_names = filter(x -> length(get_data_values(b.block_stack[end])[x]) == 1, all_names) + # Remove any whose first and only entry is 'missing' + drop_names = filter(x -> ismissing(get_data_values(b.block_stack[end])[x][1]), all_names) + [delete!(b.block_stack[end], x) for x in drop_names] + final_frame = pop!(b.block_stack) + blockname = get_block_code(a) + b.block_stack[end].save_frames[blockname] = final_frame + if b.verbose + println("Frame $blockname is finished") + end + return 0 +end + +handle_loop_start(a, b)::Cint = 0 + +function handle_loop_end(a::Ptr{cif_loop_tp}, b)::Cint + if b.verbose + println("Loop header $(keys(a))") + end + # ignore missing values + loop_names = lowercase.(keys(a)) + not_missing = filter(x -> any(y -> !ismissing(y), get_data_values(b.block_stack[end])[x]), loop_names) + create_loop!(b.block_stack[end], not_missing) + # and remove the data + missing_ones = setdiff(Set(loop_names), not_missing) + #println("Removing $missing_ones from loop") + [delete!(b.block_stack[end], x) for x in missing_ones] + return 0 +end + +handle_packet_start(a, b)::Cint = 0 + +handle_packet_end(a, b)::Cint = 0 + +function handle_item(a::Ptr{UInt16}, b::cif_value_tp_ptr, c)::Cint + a_as_uchar = Uchar(a) + val = "" + keyname = make_jl_string(a_as_uchar) + if c.verbose + println("Processing name $keyname") + end + current_block = c.block_stack[end] + syntax_type = get_syntactical_type(b) + if syntax_type == cif_value_tp_ptr + val = String(b) + elseif syntax_type == cif_list + val = cif_list(b) + elseif syntax_type == cif_table + val = cif_table(b) + elseif syntax_type == Nothing + val = nothing + elseif syntax_type == Missing + val = missing + else + error("") + end + if c.verbose + if !ismissing(val) && val !== nothing + println("With value $val") + elseif ismissing(val) + println("With value ?") + else + println("With value .") + end + end + lc_keyname = lowercase(keyname) + if !(lc_keyname in keys(get_data_values(current_block))) + get_data_values(current_block)[lc_keyname] = [val] + else + push!(get_data_values(current_block)[lc_keyname], val) + end + return 0 +end + +default_options(path; verbose=false) = begin + handle_cif_start_c = @cfunction(handle_cif_start, Cint, (cif_tp_ptr, Ref{cif_builder_context})) + handle_cif_end_c = @cfunction(handle_cif_end, Cint, (cif_tp_ptr, Ref{cif_builder_context})) + handle_block_start_c = @cfunction(handle_block_start, Cint, (cif_container_tp_ptr, Ref{cif_builder_context})) + handle_block_end_c = @cfunction(handle_block_end, Cint, (cif_container_tp_ptr, Ref{cif_builder_context})) + handle_frame_start_c = @cfunction(handle_frame_start, Cint, (cif_container_tp_ptr, Ref{cif_builder_context})) + handle_frame_end_c = @cfunction(handle_frame_end, Cint, (cif_container_tp_ptr, Ref{cif_builder_context})) + handle_loop_start_c = @cfunction(handle_loop_start, Cint, (cif_loop_tp_ptr, Ref{cif_builder_context})) + handle_loop_end_c = @cfunction(handle_loop_end, Cint, (Ptr{cif_loop_tp}, Ref{cif_builder_context})) + handle_packet_start_c = @cfunction(handle_packet_start, Cint, (Ptr{cif_packet_tp}, Ref{cif_builder_context})) + handle_packet_end_c = @cfunction(handle_packet_end, Cint, (Ptr{cif_packet_tp}, Ref{cif_builder_context})) + handle_item_c = @cfunction(handle_item, Cint, (Ptr{UInt16}, cif_value_tp_ptr, Ref{cif_builder_context})) + handlers = cif_handler_tp( + handle_cif_start_c, + handle_cif_end_c, + handle_block_start_c, + handle_block_end_c, + handle_frame_start_c, + handle_frame_end_c, + handle_loop_start_c, + handle_loop_end_c, + handle_packet_start_c, + handle_packet_end_c, + handle_item_c, + ) + context = cif_builder_context(Dict(), CifContainer[], path, verbose) + p_opts = cif_parse_opts_s(0, C_NULL, 0, 1, 1, 1, C_NULL, C_NULL, Ref(handlers), C_NULL, C_NULL, C_NULL, C_NULL, context) + return p_opts +end + +