/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 |