diff --git a/modules/io/pymod/export_mmcif_io.cc b/modules/io/pymod/export_mmcif_io.cc index ae6e956fa612f8b9c83575696c48abdea4b0fcc5..cb12690443525ac61625e1f93d50aaf3e76c1646 100644 --- a/modules/io/pymod/export_mmcif_io.cc +++ b/modules/io/pymod/export_mmcif_io.cc @@ -26,6 +26,7 @@ using namespace boost::python; #include <ost/io/mol/io_profile.hh> #include <ost/io/mol/mmcif_reader.hh> #include <ost/io/mol/mmcif_info.hh> +#include <ost/io/mol/mmcif_writer.hh> #include <ost/io/mmcif_str.hh> using namespace ost; using namespace ost::io; @@ -75,6 +76,11 @@ void export_mmcif_io() return_value_policy<copy_const_reference>())) ; + class_<MMCifWriter, boost::noncopyable>("MMCifWriter", init<const String&, const IOProfile&>()) + .def("Process_atom_site", &MMCifWriter::Process_atom_site) + .def("Write", &MMCifWriter::Write) + ; + enum_<MMCifInfoCitation::MMCifInfoCType>("MMCifInfoCType") .value("Journal", MMCifInfoCitation::JOURNAL) .value("Book", MMCifInfoCitation::BOOK) diff --git a/modules/io/src/mol/CMakeLists.txt b/modules/io/src/mol/CMakeLists.txt index 5f17d12bc4ba6d5c991275b859ce1330253f5551..8d242c3e75422c451d2b9a9b8d13a7b0063a9434 100644 --- a/modules/io/src/mol/CMakeLists.txt +++ b/modules/io/src/mol/CMakeLists.txt @@ -17,8 +17,10 @@ chemdict_parser.cc io_profile.cc dcd_io.cc star_parser.cc +star_writer.cc mmcif_reader.cc mmcif_info.cc +mmcif_writer.cc pdb_str.cc sdf_str.cc mmcif_str.cc @@ -29,9 +31,12 @@ PARENT_SCOPE set(OST_IO_MOL_HEADERS chemdict_parser.hh +star_base.hh star_parser.hh +star_writer.hh mmcif_reader.hh mmcif_info.hh +mmcif_writer.hh io_profile.hh dcd_io.hh entity_io_crd_handler.hh diff --git a/modules/io/src/mol/mmcif_writer.cc b/modules/io/src/mol/mmcif_writer.cc new file mode 100644 index 0000000000000000000000000000000000000000..c266d01c20762e32877bed4c829606c04a83c33a --- /dev/null +++ b/modules/io/src/mol/mmcif_writer.cc @@ -0,0 +1,377 @@ +//------------------------------------------------------------------------------ +// This file is part of the OpenStructure project <www.openstructure.org> +// +// Copyright (C) 2008-2023 by the OpenStructure authors +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation; either version 3.0 of the License, or (at your option) +// any later version. +// This library is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +// details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +//------------------------------------------------------------------------------ + +#include <ost/mol/chem_class.hh> + +#include <ost/io/mol/mmcif_writer.hh> + + + +namespace { + + // generates as many chain names as you want (potentially multiple characters) + struct ChainNameGenerator{ + ChainNameGenerator() { + chain_names = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; + n_chain_names = chain_names.size(); + indices.push_back(-1); + } + + String Get() { + int idx = indices.size() - 1; + indices[idx] += 1; + bool more_digits = false; + while(idx >= 0) { + if(indices[idx] >= n_chain_names) { + indices[idx] = 0; + if(idx>0) { + indices[idx-1] += 1; + --idx; + } else { + more_digits = true; + break; + } + } else { + break; + } + } + if(more_digits) { + indices.insert(indices.begin(), 0); + } + String ch_name(indices.size(), 'X'); + for(uint i = 0; i < indices.size(); ++i) { + ch_name[i] = chain_names[indices[i]]; + } + return ch_name; + } + + void Reset() { + indices.clear(); + indices.push_back(-1); + } + + String chain_names; + int n_chain_names; + std::vector<int> indices; + }; +} + +namespace ost { namespace io { + +MMCifWriter::MMCifWriter(const String& filename, const IOProfile& profile): + StarWriter(filename), + profile_(profile), + atom_site_(NULL) { } + +MMCifWriter::~MMCifWriter() { + if(atom_site_ != NULL) { + delete atom_site_; + } +} + +void MMCifWriter::Process_atom_site(const ost::mol::EntityHandle& ent) { + + this->Setup_atom_site_(); + + std::vector<std::vector<ost::mol::ResidueHandle> > L_chains; // L_PEPTIDE_LINKING + std::vector<std::vector<ost::mol::ResidueHandle> > D_chains; // D_PEPTIDE_LINKING + std::vector<std::vector<ost::mol::ResidueHandle> > P_chains; // PEPTIDE_LINKING + std::vector<std::vector<ost::mol::ResidueHandle> > R_chains; // RNA_LINKING + std::vector<std::vector<ost::mol::ResidueHandle> > S_chains; // DNA_LINKING + std::vector<std::vector<ost::mol::ResidueHandle> > X_chains; // L_SACCHARIDE + std::vector<std::vector<ost::mol::ResidueHandle> > Y_chains; // D_SACCHARIDE + std::vector<std::vector<ost::mol::ResidueHandle> > W_chains; // WATER + std::vector<ost::mol::ResidueHandle> N_chains; // NON_POLYMER (1 res per chain) + std::vector<ost::mol::ResidueHandle> U_chains; // UNKNOWN (1 res per chain) + + ost::mol::ChainHandleList chain_list = ent.GetChainList(); + for(auto ch: chain_list) { + + ost::mol::ResidueHandleList res_list = ch.GetResidueList(); + + // we don't just go for chain type here... + // just think of PDB entries that have a polypeptide, water and a ligand + // in the same chain... + + bool has_l_peptide_linking = false; + bool has_d_peptide_linking = false; + bool has_peptide_linking = false; + bool has_rna_linking = false; + bool has_dna_linking = false; + bool has_l_saccharide = false; + bool has_d_saccharide = false; + bool has_water = false; + for(auto res: res_list) { + + // Peptide chains must not mix L_PEPTIDE_LINKING AND D_PEPTIDE_LINKING + if(res.GetChemClass() == ost::mol::ChemClass::D_PEPTIDE_LINKING) { + if(has_l_peptide_linking) { + throw ost::io::IOException("Cannot write mmCIF when same chain " + "contains D- and L-peptides"); + } + has_d_peptide_linking = true; + } + if(res.GetChemClass() == ost::mol::ChemClass::L_PEPTIDE_LINKING) { + if(has_d_peptide_linking) { + throw ost::io::IOException("Cannot write mmCIF when same chain " + "contains D- and L-peptides"); + } + has_l_peptide_linking = true; + } + + if(res.GetChemClass() == ost::mol::ChemClass::PEPTIDE_LINKING) { + has_peptide_linking = true; + } + + if(res.GetChemClass() == ost::mol::ChemClass::RNA_LINKING) { + has_rna_linking = true; + } + + if(res.GetChemClass() == ost::mol::ChemClass::DNA_LINKING) { + has_dna_linking = true; + } + + if(res.GetChemClass() == ost::mol::ChemClass::L_SACCHARIDE) { + has_l_saccharide = true; + } + + if(res.GetChemClass() == ost::mol::ChemClass::D_SACCHARIDE) { + has_d_saccharide = true; + } + + if(res.GetChemClass() == ost::mol::ChemClass::WATER) { + has_water = true; + } + } + + // if there is any L-peptide or D-peptide, all peptides without + // chiral center get assigned to it. No need for specific chain. + if(has_l_peptide_linking || has_d_peptide_linking) { + has_peptide_linking = false; + } + + if(has_l_peptide_linking) { + L_chains.push_back(ost::mol::ResidueHandleList()); + } + + if(has_d_peptide_linking) { + D_chains.push_back(ost::mol::ResidueHandleList()); + } + + if(has_peptide_linking) { + P_chains.push_back(ost::mol::ResidueHandleList()); + } + + if(has_rna_linking) { + R_chains.push_back(ost::mol::ResidueHandleList()); + } + + if(has_dna_linking) { + S_chains.push_back(ost::mol::ResidueHandleList()); + } + + if(has_l_saccharide) { + X_chains.push_back(ost::mol::ResidueHandleList()); + } + + if(has_d_saccharide) { + Y_chains.push_back(ost::mol::ResidueHandleList()); + } + + if(has_water) { + W_chains.push_back(ost::mol::ResidueHandleList()); + } + + for(auto res: res_list) { + if(res.GetChemClass().IsPeptideLinking()) { + if(has_l_peptide_linking) { + L_chains.back().push_back(res); + } else if(has_d_peptide_linking) { + D_chains.back().push_back(res); + } else { + P_chains.back().push_back(res); + } + } else if(res.GetChemClass() == ost::mol::ChemClass::RNA_LINKING) { + R_chains.back().push_back(res); + } else if(res.GetChemClass() == ost::mol::ChemClass::DNA_LINKING) { + S_chains.back().push_back(res); + } else if(res.GetChemClass() == ost::mol::ChemClass::L_SACCHARIDE) { + X_chains.back().push_back(res); + } else if(res.GetChemClass() == ost::mol::ChemClass::D_SACCHARIDE) { + Y_chains.back().push_back(res); + } else if(res.GetChemClass() == ost::mol::ChemClass::WATER) { + W_chains.back().push_back(res); + } else if(res.GetChemClass() == ost::mol::ChemClass::NON_POLYMER) { + N_chains.push_back(res); + } else if(res.GetChemClass() == ost::mol::ChemClass::UNKNOWN) { + U_chains.push_back(res); + } else { + // TODO: make error message more insightful... + throw ost::io::IOException("Unsupported chem class..."); + } + } + } + + ChainNameGenerator chain_name_gen; + + // process L_PEPTIDE_LINKING + for(auto res_list: L_chains) { + String chain_name = chain_name_gen.Get(); + Feed_atom_site_(chain_name, 0, res_list); + } + + // process D_PEPTIDE_LINKING + for(auto res_list: D_chains) { + String chain_name = chain_name_gen.Get(); + Feed_atom_site_(chain_name, 0, res_list); + } + + // process PEPTIDE_LINKING + for(auto res_list: P_chains) { + String chain_name = chain_name_gen.Get(); + Feed_atom_site_(chain_name, 0, res_list); + } + + // process RNA_LINKING + for(auto res_list: R_chains) { + String chain_name = chain_name_gen.Get(); + Feed_atom_site_(chain_name, 0, res_list); + } + + // process DNA_LINKING + for(auto res_list: S_chains) { + String chain_name = chain_name_gen.Get(); + Feed_atom_site_(chain_name, 0, res_list); + } + + // process L_SACHARIDE + for(auto res_list: X_chains) { + String chain_name = chain_name_gen.Get(); + Feed_atom_site_(chain_name, 0, res_list); + } + + // process D_SACHARIDE + for(auto res_list: Y_chains) { + String chain_name = chain_name_gen.Get(); + Feed_atom_site_(chain_name, 0, res_list); + } + + // process WATER + for(auto res_list: W_chains) { + String chain_name = chain_name_gen.Get(); + Feed_atom_site_(chain_name, 0, res_list); + } + + // process NON_POLYMER + for(auto res: N_chains) { + ost::mol::ResidueHandleList res_list; + res_list.push_back(res); + String chain_name = chain_name_gen.Get(); + Feed_atom_site_(chain_name, 0, res_list); + } + + // process UNKNOWN + for(auto res: N_chains) { + ost::mol::ResidueHandleList res_list; + res_list.push_back(res); + String chain_name = chain_name_gen.Get(); + Feed_atom_site_(chain_name, 0, res_list); + } + + // finalize + this->Push(atom_site_); +} + +void MMCifWriter::Setup_atom_site_() { + StarLoopDesc desc; + desc.SetCategory("_atom_site"); + desc.Add("group_PDB"); + desc.Add("type_symbol"); + desc.Add("label_atom_id"); + desc.Add("label_comp_id"); + desc.Add("label_asym_id"); + desc.Add("label_entity_id"); + desc.Add("label_seq_id"); + desc.Add("label_alt_id"); + desc.Add("Cartn_x"); + desc.Add("Cartn_y"); + desc.Add("Cartn_z"); + desc.Add("occupancy"); + desc.Add("B_iso_or_equiv"); + desc.Add("auth_seq_id"); + desc.Add("auth_asym_id"); + desc.Add("id"); + atom_site_ = new StarLoop(desc); +} + +void MMCifWriter::Feed_atom_site_(const String& label_asym_id, + int label_entity_id, + const ost::mol::ResidueHandleList& res_list) { + + int label_seq_id = 1; + for(auto res: res_list) { + String comp_id = res.GetName(); + ost::mol::AtomHandleList at_list = res.GetAtomList(); + String auth_asym_id = res.GetChain().GetName(); + String auth_seq_id = res.GetNumber().AsString(); + for(auto at: at_list) { + std::vector<StarLoopDataItemDO> at_data; + // group_PDB + if(at.IsHetAtom()) { + at_data.push_back(StarLoopDataItemDO("HETATM")); + } else { + at_data.push_back(StarLoopDataItemDO("ATOM")); + } + // type_symbol + at_data.push_back(StarLoopDataItemDO(at.GetElement())); + // label_atom_id + at_data.push_back(StarLoopDataItemDO(at.GetName())); + // label_comp_id + at_data.push_back(StarLoopDataItemDO(comp_id)); + // label_asym_id + at_data.push_back(StarLoopDataItemDO(label_asym_id)); + // label_entity_id + at_data.push_back(StarLoopDataItemDO(label_entity_id)); + // label_seq_id + at_data.push_back(StarLoopDataItemDO(label_seq_id)); + // label_alt_id + at_data.push_back(StarLoopDataItemDO(".")); + // Cartn_x + at_data.push_back(StarLoopDataItemDO(at.GetPos().GetX(), 3)); + // Cartn_y + at_data.push_back(StarLoopDataItemDO(at.GetPos().GetY(), 3)); + // Cartn_z + at_data.push_back(StarLoopDataItemDO(at.GetPos().GetZ(), 3)); + // occupancy + at_data.push_back(StarLoopDataItemDO(at.GetOccupancy(), 2)); + // B_iso_or_equiv + at_data.push_back(StarLoopDataItemDO(at.GetBFactor(), 2)); + // auth_seq_id + at_data.push_back(StarLoopDataItemDO(auth_seq_id)); + // auth_asym_id + at_data.push_back(StarLoopDataItemDO(auth_asym_id)); + // id + at_data.push_back(StarLoopDataItemDO(atom_site_->GetN())); + atom_site_->AddData(at_data); + } + ++label_seq_id; + } +} + +}} // ns diff --git a/modules/io/src/mol/mmcif_writer.hh b/modules/io/src/mol/mmcif_writer.hh new file mode 100644 index 0000000000000000000000000000000000000000..f5b5ece376fec5b801c0ccae66ca17e1853f1c09 --- /dev/null +++ b/modules/io/src/mol/mmcif_writer.hh @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +// This file is part of the OpenStructure project <www.openstructure.org> +// +// Copyright (C) 2008-2023 by the OpenStructure authors +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation; either version 3.0 of the License, or (at your option) +// any later version. +// This library is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +// details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +//------------------------------------------------------------------------------ +#ifndef OST_IO_MMCIF_WRITER_HH +#define OST_IO_MMCIF_WRITER_HH + +#include <fstream> + +#include <ost/mol/entity_handle.hh> + +#include <ost/io/mol/mmcif_info.hh> +#include <ost/io/mol/io_profile.hh> +#include <ost/io/mol/star_writer.hh> + +namespace ost { namespace io { + +class DLLEXPORT_OST_IO MMCifWriter : public StarWriter { +public: + + MMCifWriter(const String& filename, const IOProfile& profile); + + virtual ~MMCifWriter(); + + void Process_atom_site(const ost::mol::EntityHandle& ent); + +private: + + void Setup_atom_site_(); + void Feed_atom_site_(const String& label_asym_id, + int label_entity_id, + const ost::mol::ResidueHandleList& res_list); + + IOProfile profile_; + StarLoop* atom_site_; +}; + + +}} // ns + +#endif \ No newline at end of file diff --git a/modules/io/src/mol/star_base.hh b/modules/io/src/mol/star_base.hh new file mode 100644 index 0000000000000000000000000000000000000000..2433a0af6bb9deed53657bc636f7a60982c73b11 --- /dev/null +++ b/modules/io/src/mol/star_base.hh @@ -0,0 +1,307 @@ +//------------------------------------------------------------------------------ +// This file is part of the OpenStructure project <www.openstructure.org> +// +// Copyright (C) 2008-2023 by the OpenStructure authors +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation; either version 3.0 of the License, or (at your option) +// any later version. +// This library is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +// details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +//------------------------------------------------------------------------------ +#ifndef OST_IO_STAR_BASE_HH +#define OST_IO_STAR_BASE_HH + +#include <map> +#include <algorithm> +#include <ost/string_ref.hh> +#include <ost/io/io_exception.hh> + +namespace{ + // float to string with specified number of decimals + void fts(Real f, int decimals, String& s) { + char data[20]; + size_t len; + switch(decimals){ + case 0: + len = std::snprintf(data, sizeof(data), "%.0f", f); + break; + case 1: + len = std::snprintf(data, sizeof(data), "%.1f", f); + break; + case 2: + len = std::snprintf(data, sizeof(data), "%.2f", f); + break; + case 3: + len = std::snprintf(data, sizeof(data), "%.3f", f); + break; + case 4: + len = std::snprintf(data, sizeof(data), "%.4f", f); + break; + case 5: + len = std::snprintf(data, sizeof(data), "%.5f", f); + break; + case 6: + len = std::snprintf(data, sizeof(data), "%.6f", f); + break; + default: + throw ost::io::IOException("Max decimals in float conversion: 6"); + } + + if(len < 0 || len > 20) { + throw ost::io::IOException("float conversion failed"); + } + s.assign(data, len); + } +} + +namespace ost { namespace io { + + +typedef enum { + STAR_DIAG_WARNING, + STAR_DIAG_ERROR +} StarDiagType; + + +class DLLEXPORT_OST_IO StarObject { +public: + virtual ~StarObject() { } + virtual void ToStream(std::ostream& s) = 0; +}; + +class DLLEXPORT_OST_IO StarDataItem : public StarObject{ +public: + StarDataItem(const StringRef& category, const StringRef& name, + const StringRef& value): + category_(category), name_(name), value_(value) + { } + + virtual void ToStream(std::ostream& s) { + s << category_ << '.' << name_ << ' ' << value_ << std::endl; + } + + const StringRef& GetCategory() const { return category_; } + const StringRef& GetName() const { return name_; } + const StringRef& GetValue() const { return value_; } +private: + StringRef category_; + StringRef name_; + StringRef value_; +}; + + +class DLLEXPORT_OST_IO StarDataItemDO : public StarObject{ + // basically a copy of StarDataItem but with data ownership + // Problem with StringRef: It's purely pointer based + // => data needs to reside elsewhere +public: + StarDataItemDO(const String& category, const String& name, + const String& value): + category_(category), name_(name) + { + // cases we still need to deal with: + // - special characters in strings (put in quotation marks) + // - long strings (semicolon based syntax) + // see https://mmcif.wwpdb.org/docs/tutorials/mechanics/pdbx-mmcif-syntax.html + if(value == "") { + value_ = "."; + } else { + value_ = value; + } + } + + StarDataItemDO(const String& category, const String& name, + Real value, int decimals): + category_(category), name_(name) + { + fts(value, decimals, value_); + } + + StarDataItemDO(const String& category, const String& name, + int value): + category_(category), name_(name) + { + value_ = std::to_string(value); + } + + virtual void ToStream(std::ostream& s) { + s << category_ << '.' << name_ << ' ' << value_ << std::endl; + } + + const String& GetCategory() const { return category_; } + const String& GetName() const { return name_; } + const String& GetValue() const { return value_; } +private: + String category_; + String name_; + String value_; +}; + +class DLLEXPORT_OST_IO StarDataCategoryDO : public StarObject { +public: + StarDataCategoryDO(const String& category): category_(category) {} + + void Add(const StarDataItemDO& data_item) { + if(data_item.GetCategory() != category_) { + throw ost::io::IOException("category mismatch"); + } + data_items_.push_back(data_item); + } + + virtual void ToStream(std::ostream& s) { + for(auto it = data_items_.begin(); it != data_items_.end(); ++it) { + it->ToStream(s); + } + } + +private: + String category_; + std::vector<StarDataItemDO> data_items_; +}; + +class DLLEXPORT_OST_IO StarLoopDesc : public StarObject { +public: + StarLoopDesc(): + category_("") + { } + + int GetIndex(const String& name) const + { + std::map<String, int>::const_iterator i=index_map_.find(name); + return i==index_map_.end() ? -1 : i->second; + } + + void SetCategory(const StringRef& category) + { + category_=category.str(); + } + + void SetCategory(const String& category) + { + category_=category; + } + + void Add(const StringRef& name) + { + index_map_.insert(std::make_pair(name.str(), index_map_.size())); + } + void Add(const String& name) + { + index_map_.insert(std::make_pair(name, index_map_.size())); + } + size_t GetSize() const + { + return index_map_.size(); + } + void Clear() + { + category_.clear(); + index_map_.clear(); + } + + virtual void ToStream(std::ostream& s) { + std::vector<std::pair<int, String> > tmp; + for(auto it = index_map_.begin(); it != index_map_.end(); ++it) { + tmp.push_back(std::make_pair(it->second, it->first)); + } + std::sort(tmp.begin(), tmp.end()); + for(auto it = tmp.begin(); it != tmp.end(); ++it) { + s << category_ << "." << it->second << std::endl; + } + } + + const String& GetCategory() const { return category_; } +private: + String category_; + std::map<String, int> index_map_; +}; + +class DLLEXPORT_OST_IO StarLoopDataItemDO{ +public: + + StarLoopDataItemDO(const String& value) { + // cases we still need to deal with: + // - special characters in strings (put in quotation marks) + // - long strings (semicolon based syntax) + // see https://mmcif.wwpdb.org/docs/tutorials/mechanics/pdbx-mmcif-syntax.html + if(value == "") { + value_ = "."; + } else { + value_ = value; + } + } + + StarLoopDataItemDO(Real value, int decimals) { + fts(value, decimals, value_); + } + + StarLoopDataItemDO(int value) { + value_ = std::to_string(value); + } + + const String& GetValue() const { return value_; } + + virtual void ToStream(std::ostream& s) { + s << value_; + } + +private: + String value_; +}; + +class DLLEXPORT_OST_IO StarLoop: public StarObject { +public: + + StarLoop() { } + + StarLoop(const StarLoopDesc& desc): desc_(desc) { } + + void SetDesc(const StarLoopDesc& desc) { + if(!data_.empty()) { + throw ost::io::IOException("Can only set new StarLoop desc in " + "in empty loop"); + } + desc_ = desc; + } + + void AddData(const std::vector<StarLoopDataItemDO>& data) { + if(data.size() != desc_.GetSize()) { + throw ost::io::IOException("Invalid data size when adding to StarLoop"); + } + data_.insert(data_.end(), data.begin(), data.end()); + } + + int GetN() { + return data_.size() / desc_.GetSize(); + } + + virtual void ToStream(std::ostream& s) { + s << "loop_" << std::endl; + desc_.ToStream(s); + int desc_size = desc_.GetSize(); + for(size_t i = 0; i < data_.size(); ++i) { + data_[i].ToStream(s); + if((i+1) % desc_size == 0) { + s << std::endl; + } else { + s << ' '; + } + } + } + +private: + StarLoopDesc desc_; + std::vector<StarLoopDataItemDO> data_; +}; + +}} // ns + +#endif diff --git a/modules/io/src/mol/star_parser.hh b/modules/io/src/mol/star_parser.hh index bc6a3947a6694ec83e8e3fd699d30223cc87359f..0d13b1a50ab4be12b2da85c72adf725f4bee4d59 100644 --- a/modules/io/src/mol/star_parser.hh +++ b/modules/io/src/mol/star_parser.hh @@ -31,70 +31,10 @@ #include <map> #include <ost/string_ref.hh> #include <ost/io/module_config.hh> +#include <ost/io/mol/star_base.hh> namespace ost { namespace io { - -typedef enum { - STAR_DIAG_WARNING, - STAR_DIAG_ERROR -} StarDiagType; - - -class DLLEXPORT_OST_IO StarDataItem { -public: - StarDataItem(const StringRef& category, const StringRef& name, - const StringRef& value): - category_(category), name_(name), value_(value) - { } - const StringRef& GetCategory() const { return category_; } - const StringRef& GetName() const { return name_; } - const StringRef& GetValue() const { return value_; } -private: - StringRef category_; - StringRef name_; - StringRef value_; -}; - -class DLLEXPORT_OST_IO StarLoopDesc { -public: - StarLoopDesc(): - category_("") - { } - - int GetIndex(const String& name) const - { - std::map<String, int>::const_iterator i=index_map_.find(name); - return i==index_map_.end() ? -1 : i->second; - } - - void SetCategory(const StringRef& category) - { - category_=category.str(); - } - - - - void Add(const StringRef& name) - { - index_map_.insert(std::make_pair(name.str(), index_map_.size())); - } - size_t GetSize() const - { - return index_map_.size(); - } - void Clear() - { - category_.clear(); - index_map_.clear(); - } - - const String& GetCategory() const { return category_; } -private: - String category_; - std::map<String, int> index_map_; -}; - /// \brief parser for the STAR file format /// /// \section star_format STAR format description diff --git a/modules/io/src/mol/star_writer.cc b/modules/io/src/mol/star_writer.cc new file mode 100644 index 0000000000000000000000000000000000000000..327d005631c6e656e1e89e954cb9446c8e78c33d --- /dev/null +++ b/modules/io/src/mol/star_writer.cc @@ -0,0 +1,56 @@ +//------------------------------------------------------------------------------ +// This file is part of the OpenStructure project <www.openstructure.org> +// +// Copyright (C) 2008-2023 by the OpenStructure authors +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation; either version 3.0 of the License, or (at your option) +// any later version. +// This library is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +// details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +//------------------------------------------------------------------------------ + +#include <ost/io/mol/star_writer.hh> + +namespace ost{ namespace io{ + +StarWriter::StarWriter(std::ostream& stream): filename_("<stream>"), + file_open_(true) { + if(!stream) { + file_open_ = false; + } + stream_.push(stream); +} + + +StarWriter::StarWriter(const String& filename): filename_(filename), + file_open_(true), + fstream_(filename.c_str()) { + if (!fstream_) { + file_open_ = false; + } + stream_.push(fstream_); +} + +void StarWriter::Write(const String& data_name) { + if (!file_open_) { + throw IOException("yolo"); + } + + // write data header + stream_ << "data_" << data_name << std::endl; + + for(auto star_obj : categories_to_write_) { + star_obj->ToStream(stream_); + stream_ << String("#") << std::endl; + } +} + +}} // ns diff --git a/modules/io/src/mol/star_writer.hh b/modules/io/src/mol/star_writer.hh new file mode 100644 index 0000000000000000000000000000000000000000..f858e021a81ff6e0e012dd29c7c6f4291258c1ac --- /dev/null +++ b/modules/io/src/mol/star_writer.hh @@ -0,0 +1,50 @@ +//------------------------------------------------------------------------------ +// This file is part of the OpenStructure project <www.openstructure.org> +// +// Copyright (C) 2008-2023 by the OpenStructure authors +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation; either version 3.0 of the License, or (at your option) +// any later version. +// This library is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +// details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +//------------------------------------------------------------------------------ +#ifndef OST_IO_STAR_WRITER_HH +#define OST_IO_STAR_WRITER_HH + +#include <map> +#include <fstream> +#include <boost/iostreams/filtering_stream.hpp> +#include <boost/iostreams/filter/gzip.hpp> +#include <ost/string_ref.hh> +#include <ost/io/mol/star_base.hh> + + +namespace ost { namespace io { + +class DLLEXPORT_OST_IO StarWriter { +public: + StarWriter(std::ostream& stream); + StarWriter(const String& filename); + virtual ~StarWriter() { } + + void Push(StarObject* obj) { categories_to_write_.push_back(obj); } + void Write(const String& data_name); +private: + String filename_; + bool file_open_; + std::ofstream fstream_; + boost::iostreams::filtering_stream<boost::iostreams::output> stream_; + std::vector<StarObject*> categories_to_write_; +}; + +}} // ns + +#endif