Coverage Report

Created: 2026-10-17 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openbabel/src/ops/sort.cpp
Line
Count
Source
1
/**********************************************************************
2
sort.cpp - A OBOp for sorting molecules during conversion.
3
4
Copyright (C) 2009 by Chris Morley
5
6
This file is part of the Open Babel project.
7
For more information, see <http://openbabel.org/>
8
9
This program is free software; you can redistribute it and/or modify
10
it under the terms of the GNU General Public License as published by
11
the Free Software Foundation version 2 of the License.
12
13
This program is distributed in the hope that it will be useful,
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
GNU General Public License for more details.
17
***********************************************************************/
18
#include <openbabel/babelconfig.h>
19
#include <openbabel/op.h>
20
#include <openbabel/mol.h>
21
#include <openbabel/obconversion.h>
22
#include <openbabel/descriptor.h>
23
#include <openbabel/obutil.h>
24
#include "deferred.h"
25
#include <set>
26
#include <algorithm>
27
28
namespace OpenBabel
29
{
30
31
template<class T>
32
struct Order
33
{
34
0
  Order(OBDescriptor* pDesc, bool rev) : _pDesc(pDesc), _rev(rev){}
Unexecuted instantiation: OpenBabel::Order<double>::Order(OpenBabel::OBDescriptor*, bool)
Unexecuted instantiation: OpenBabel::Order<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::Order(OpenBabel::OBDescriptor*, bool)
35
  bool operator()(std::pair<OBBase*,T> p1, std::pair<OBBase*,T> p2) const
36
0
  {
37
0
    return _rev ?
38
0
      _pDesc->Order(p2.second, p1.second) :
39
0
      _pDesc->Order(p1.second, p2.second);
40
0
  }
Unexecuted instantiation: OpenBabel::Order<double>::operator()(std::__1::pair<OpenBabel::OBBase*, double>, std::__1::pair<OpenBabel::OBBase*, double>) const
Unexecuted instantiation: OpenBabel::Order<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::operator()(std::__1::pair<OpenBabel::OBBase*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::pair<OpenBabel::OBBase*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >) const
41
  OBDescriptor* _pDesc;
42
  bool _rev;
43
};
44
//*****************************************************************
45
class OpSort : public OBOp
46
{
47
public:
48
12
  OpSort(const char* ID) : OBOp(ID, false)
49
12
  {
50
12
    OBConversion::RegisterOptionParam(ID, nullptr, 1, OBConversion::GENOPTIONS);
51
12
  }
52
53
0
  const char* Description() override { return "<desc> Sort by descriptor(~desc for reverse)"
54
0
    "\n Follow descriptor with + to also add it to the title, e.g. MW+ "
55
0
    "\n Custom ordering is possible; see inchi descriptor"; }
56
57
0
  bool WorksWith(OBBase* pOb) const override { return dynamic_cast<OBMol*>(pOb) != nullptr; }
58
  bool Do(OBBase* pOb, const char* OptionText, OpMap* pmap, OBConversion* pConv) override;
59
  bool ProcessVec(std::vector<OBBase*>& vec) override;
60
private:
61
  OBDescriptor* _pDesc;
62
  std::string _pDescOption;
63
  bool _rev;
64
  bool _addDescToTitle;
65
};
66
67
/////////////////////////////////////////////////////////////////
68
OpSort theOpSort("sort"); //Global instance
69
70
/////////////////////////////////////////////////////////////////
71
bool OpSort::Do(OBBase* /*pOb*/, const char* OptionText, OpMap* /*pmap*/, OBConversion* pConv)
72
0
{
73
0
  if (!OptionText)
74
0
    OptionText = "";
75
0
  if(pConv && pConv->IsFirstInput())
76
0
  {
77
0
    _rev=false;
78
0
    if(*OptionText=='~')
79
0
    {
80
0
      _rev=true;
81
0
      ++OptionText;
82
0
    }
83
84
0
    const char* pLast = OptionText + strlen(OptionText)-1;
85
0
    _addDescToTitle = *(OptionText + strlen(OptionText)-1)=='+';//last char
86
0
    if(_addDescToTitle)
87
0
      *const_cast<char*>(pLast)='\0';
88
89
0
    std::istringstream optionStream(OptionText);
90
0
    std::pair<std::string,std::string> spair = OBDescriptor::GetIdentifier(optionStream);
91
0
    _pDesc = OBDescriptor::FindType(spair.first.c_str());
92
0
    if(!_pDesc)
93
0
    {
94
0
     obErrorLog.ThrowError(__FUNCTION__,
95
0
              std::string("Unknown descriptor ") + OptionText, obError, onceOnly);
96
0
     return false;
97
0
    }
98
0
    _pDescOption = spair.second;
99
0
    _pDesc->Init();//needed  to clear cache of InChIFilter
100
101
102
    //Make a deferred format and divert the output to it
103
0
    new DeferredFormat(pConv, this); //it will delete itself
104
0
  }
105
0
  return true;
106
0
}
107
108
//****************************************************************
109
bool OpSort::ProcessVec(std::vector<OBBase*>& vec)
110
0
{
111
  // Make a vector containing both the OBBase* and the descriptor value and the sort it
112
0
  if(!IsNan(_pDesc->Predict(vec[0], &_pDescOption)))
113
0
  {
114
    //a numerical descriptor
115
    //Copy into a pair vector
116
0
    std::vector<std::pair<OBBase*,double> > valvec;
117
0
    valvec.reserve(vec.size());
118
0
    std::vector<OBBase*>::iterator iter;
119
0
    for(iter=vec.begin();iter!=vec.end();++iter)
120
0
      valvec.push_back(std::make_pair<OBBase*,double>(&(**iter), _pDesc->Predict(*iter, &_pDescOption)));
121
122
    //Sort
123
0
    std::sort(valvec.begin(),valvec.end(), Order<double>(_pDesc, _rev));
124
125
    //Copy back
126
0
    std::vector<std::pair<OBBase*,double> >::iterator valiter;
127
0
    iter=vec.begin();
128
0
    for(valiter=valvec.begin();valiter!=valvec.end();++valiter, ++iter)
129
0
    {
130
0
      *iter = valiter->first;
131
0
      if(_addDescToTitle)
132
0
      {
133
0
        std::stringstream ss;
134
0
        ss << (*iter)->GetTitle() << ' ' << valiter->second;
135
0
        (*iter)->SetTitle(ss.str().c_str());
136
0
      }
137
0
    }
138
0
  }
139
0
  else
140
0
  {
141
    //a string descriptor
142
    //Copy into a pair vector
143
0
    std::vector<std::pair<OBBase*,std::string> > valvec;
144
0
    valvec.reserve(vec.size());
145
0
    std::vector<OBBase*>::iterator iter;
146
0
    std::string s;
147
0
    for(iter=vec.begin();iter!=vec.end();++iter)
148
0
    {
149
0
      _pDesc->GetStringValue(*iter, s, &_pDescOption);
150
0
      valvec.push_back(std::pair<OBBase*,std::string>(&(**iter), s));
151
0
    }
152
153
    //Sort
154
0
    std::sort(valvec.begin(),valvec.end(), Order<std::string>(_pDesc, _rev));
155
156
    //Copy back
157
0
    std::vector<std::pair<OBBase*,std::string> >::iterator valiter;
158
0
    iter=vec.begin();
159
0
    for(valiter=valvec.begin();valiter!=valvec.end();++valiter, ++iter)
160
0
    {
161
0
      *iter = valiter->first;
162
0
      if(_addDescToTitle)
163
0
      {
164
0
        std::stringstream ss;
165
0
        ss << (*iter)->GetTitle() << ' ' << valiter->second;
166
0
        (*iter)->SetTitle(ss.str().c_str());
167
0
      }
168
0
    }
169
0
  }
170
171
0
  return true;
172
0
}
173
/*
174
This started as a nice compact piece of code! The need to handle descriptors
175
which return either numbers or strings was originally achieved without testing
176
the type here by using LessThan() in the descriptor. But this meant that the
177
descriptor value was recalculated every time it was needed, which is inappropriate
178
for sorting. A local cache of InChI values was implemented but a more general
179
solution was needed. The values are now calculated once and stored here in a
180
vector, which stores numbers or strings and the code is extensively duplicated
181
because of this. But using templates was not much shorter because four templated
182
functions were needed, and the code more difficult to understand.
183
*/
184
185
}//namespace