/src/openbabel/src/transform.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | transform.cpp - Perform command-line requested transformations |
3 | | |
4 | | Copyright (C) 2004-2005 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 <sstream> |
20 | | #include <openbabel/mol.h> |
21 | | #include <openbabel/generic.h> |
22 | | #include <openbabel/oberror.h> |
23 | | #include <openbabel/descriptor.h> |
24 | | #include <openbabel/op.h> |
25 | | #include <openbabel/parsmart.h> |
26 | | |
27 | | #include <cstdlib> |
28 | | |
29 | | using namespace std; |
30 | | namespace OpenBabel |
31 | | { |
32 | | class OBConversion; //used only as a pointer |
33 | | |
34 | | OBBase* OBMol::DoTransformations(const std::map<std::string, std::string>* pOptions, OBConversion* pConv) |
35 | 10.7k | { |
36 | | // Perform any requested transformations |
37 | | // on a OBMol |
38 | | //The input map has option letters or name as the key and |
39 | | //any associated text as the value. |
40 | | //For normal(non-filter) transforms: |
41 | | // returns a pointer to the OBMol (this) if ok or NULL if not. |
42 | | //For filters returns a pointer to the OBMol (this) if there is a match, |
43 | | //and NULL when not and in addition the OBMol object is deleted NULL. |
44 | | |
45 | | //This is now a virtual function. The OBBase version just returns the OBMol pointer. |
46 | | //This is declared in mol.h |
47 | | |
48 | | //The filter options, s and v allow a obgrep facility. |
49 | | //Used together they must both be true to allow a molecule through. |
50 | | |
51 | | //Parse GeneralOptions |
52 | 10.7k | if(pOptions->empty()) |
53 | 10.7k | return this; |
54 | | |
55 | | // DoOps calls Do() for each of the plugin options in the map |
56 | | // It normally returns true, even if there are no options but |
57 | | // can return false if one of the options decides that the |
58 | | // molecule should not be output. If it is a filtering op, it |
59 | | // should delete the molecule itself (unlike the -s, --filter options, |
60 | | // which delete it in this function). |
61 | 0 | if(!OBOp::DoOps(this, pOptions, pConv)) |
62 | 0 | return nullptr; |
63 | | |
64 | 0 | bool ret=true; |
65 | |
|
66 | 0 | map<string,string>::const_iterator itr, itr2; |
67 | |
|
68 | 0 | if(pOptions->find("b")!=pOptions->end()) |
69 | 0 | ConvertDativeBonds(); |
70 | 0 | if(pOptions->find("B")!=pOptions->end()) |
71 | 0 | MakeDativeBonds(); |
72 | |
|
73 | 0 | if(pOptions->find("d")!=pOptions->end()) |
74 | 0 | if(!DeleteHydrogens()) |
75 | 0 | ret=false; |
76 | |
|
77 | 0 | if(pOptions->find("h")!=pOptions->end()) |
78 | 0 | if(!AddHydrogens(false, false)) |
79 | 0 | ret=false; |
80 | |
|
81 | 0 | if(pOptions->find("r")!=pOptions->end()) { |
82 | 0 | StripSalts(); |
83 | 0 | ret = true; |
84 | 0 | } |
85 | |
|
86 | 0 | itr = pOptions->find("p"); |
87 | 0 | if(itr!=pOptions->end()) { |
88 | 0 | if(pOptions->find("h")!=pOptions->end()){ |
89 | 0 | stringstream errorMsg; |
90 | 0 | errorMsg << "Both -p and -h options are set. " |
91 | 0 | << "All implicit hydrogens (-h) will be added without considering pH." |
92 | 0 | << endl; |
93 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
94 | 0 | } |
95 | 0 | else { |
96 | 0 | double pH = strtod(itr->second.c_str(), nullptr); |
97 | 0 | if(!AddHydrogens(false, true, pH)) |
98 | 0 | ret=false; |
99 | 0 | } |
100 | 0 | } |
101 | |
|
102 | 0 | if(pOptions->find("c")!=pOptions->end()) |
103 | 0 | Center(); |
104 | |
|
105 | 0 | itr = pOptions->find("title"); //Replaces title |
106 | 0 | if(itr!=pOptions->end()) |
107 | 0 | SetTitle(itr->second.c_str()); |
108 | |
|
109 | 0 | itr = pOptions->find("addtotitle"); //Appends text to title |
110 | 0 | if(itr!=pOptions->end()) |
111 | 0 | { |
112 | 0 | string title(GetTitle()); |
113 | 0 | title += itr->second; |
114 | 0 | SetTitle(title.c_str()); |
115 | 0 | } |
116 | | |
117 | | //Add an extra property to the molecule. |
118 | | //--property attribute value (no spaces in attribute) |
119 | | //or |
120 | | // --property attribute1=value1; multi word attribute2 = multi word value2; |
121 | | //For both forms, no ';' in either attribute or value. |
122 | | //For second form, no '=' in attribute or value |
123 | 0 | itr = pOptions->find("property"); |
124 | 0 | if(itr!=pOptions->end()) { |
125 | 0 | string txt(itr->second); |
126 | |
|
127 | 0 | vector<string> vec; |
128 | 0 | vector<string>::iterator it; |
129 | 0 | tokenize(vec, txt,";"); |
130 | 0 | for(it=vec.begin();it!=vec.end();++it) { |
131 | 0 | string attr, val; |
132 | 0 | string::size_type pos = it->find('='); |
133 | 0 | if(pos==string::npos) { |
134 | | //form with space |
135 | 0 | pos = it->find(' '); |
136 | 0 | if(pos==string::npos) { |
137 | 0 | obErrorLog.ThrowError(__FUNCTION__, "Missing property value", obError); |
138 | 0 | ret=false; |
139 | 0 | break; |
140 | 0 | } |
141 | 0 | } |
142 | 0 | val = it->substr(pos+1); |
143 | 0 | Trim(val); |
144 | 0 | attr = it->erase(pos); |
145 | 0 | Trim(attr); |
146 | | |
147 | | //Update value if it already exists |
148 | 0 | OBPairData* dp = dynamic_cast<OBPairData*>(GetData(attr)); |
149 | 0 | if(dp) { |
150 | 0 | dp->SetValue(val); |
151 | 0 | dp->SetOrigin(userInput); |
152 | 0 | } |
153 | 0 | else { |
154 | | // Pair did not exist; make new one |
155 | 0 | dp = new OBPairData; |
156 | 0 | dp->SetAttribute(attr); |
157 | 0 | dp->SetValue(val); |
158 | 0 | dp->SetOrigin(userInput); |
159 | 0 | SetData(dp); |
160 | 0 | } |
161 | 0 | } |
162 | 0 | } |
163 | |
|
164 | 0 | itr = pOptions->find("add"); //adds new properties from descriptors in list |
165 | 0 | if(itr!=pOptions->end()) |
166 | 0 | OBDescriptor::AddProperties(this, itr->second); |
167 | |
|
168 | 0 | itr = pOptions->find("delete"); //deletes the specified properties |
169 | 0 | if(itr!=pOptions->end()) |
170 | 0 | OBDescriptor::DeleteProperties(this, itr->second); |
171 | |
|
172 | 0 | itr = pOptions->find("append"); //Appends values of descriptors or properties to title |
173 | 0 | if(itr!=pOptions->end()) |
174 | 0 | { |
175 | 0 | string title(GetTitle()); |
176 | 0 | title += OBDescriptor::GetValues(this, itr->second); |
177 | 0 | if(ispunct(title[0])) |
178 | 0 | title[0]=' ';//a leading punct char is used only as a separator, not at start |
179 | 0 | SetTitle(Trim(title).c_str()); |
180 | 0 | } |
181 | | |
182 | | |
183 | | |
184 | | //Filter using OBDescriptor comparison and (older) SMARTS tests |
185 | | //Continue only if previous test was true. |
186 | 0 | bool fmatch = true; |
187 | 0 | itr = pOptions->find("filter"); |
188 | 0 | if(itr!=pOptions->end()) |
189 | 0 | { |
190 | 0 | std::istringstream optionText(itr->second); |
191 | 0 | fmatch = OBDescriptor::FilterCompare(this, optionText, false); |
192 | 0 | } |
193 | |
|
194 | 0 | if(fmatch) |
195 | 0 | { |
196 | 0 | itr = pOptions->find("v"); |
197 | 0 | if(itr!=pOptions->end() && !itr->second.empty()) |
198 | 0 | { |
199 | | //inverse match quoted SMARTS string which follows |
200 | 0 | OBSmartsPattern sp; |
201 | 0 | sp.Init(itr->second); |
202 | 0 | fmatch = !sp.Match(*this); //(*pmol) ; |
203 | 0 | } |
204 | 0 | } |
205 | 0 | if(fmatch) |
206 | 0 | { |
207 | 0 | itr = pOptions->find("s"); |
208 | 0 | if(itr!=pOptions->end() && !itr->second.empty()) |
209 | 0 | { |
210 | | //SMARTS filter |
211 | | //If exactmatch option set (probably in fastsearchformat) the |
212 | | //number of atoms in the pattern (passed as a string in the option text) |
213 | | //has to be the same as in the molecule. |
214 | 0 | itr2 = pOptions->find("exactmatch"); |
215 | 0 | if(itr2!=pOptions->end() && (int)NumHvyAtoms()!=atoi(itr2->second.c_str())) |
216 | 0 | fmatch=false; |
217 | 0 | else |
218 | 0 | { |
219 | | //match quoted SMARTS string which follows |
220 | 0 | OBSmartsPattern sp; |
221 | 0 | sp.Init(itr->second.c_str()); |
222 | 0 | fmatch = sp.Match(*this); |
223 | 0 | } |
224 | 0 | } |
225 | 0 | } |
226 | |
|
227 | 0 | if(!fmatch) |
228 | 0 | { |
229 | | //filter failed: delete OBMol and return NULL |
230 | 0 | delete this; |
231 | 0 | return nullptr; |
232 | 0 | } |
233 | 0 | else |
234 | 0 | { |
235 | 0 | if(ret==false) |
236 | 0 | { |
237 | 0 | obErrorLog.ThrowError(__FUNCTION__, "Error executing an option", obError); |
238 | 0 | delete this; //added 9March2006 |
239 | 0 | return nullptr; |
240 | 0 | } |
241 | 0 | else |
242 | 0 | return this; |
243 | 0 | } |
244 | 0 | } |
245 | | |
246 | | /////////////////////////////////////////////////// |
247 | | const char* OBMol::ClassDescription() |
248 | 0 | { |
249 | 0 | static string ret; |
250 | 0 | ret = "For conversions of molecules\n" |
251 | 0 | "Additional options :\n" |
252 | 0 | "-d Delete hydrogens (make implicit)\n" |
253 | 0 | "-h Add hydrogens (make explicit)\n" |
254 | 0 | "-p <pH> Add hydrogens appropriate for this pH\n" |
255 | 0 | "-b Convert dative bonds e.g.-[N+]([O-])=O to -N(=O)=O\n" |
256 | 0 | "-B Make dative bonds e.g.-[N+]([O-])=O from -N(=O)=O\n" |
257 | 0 | "-r Remove all but the largest contiguous fragment\n" |
258 | 0 | "-c Center Coordinates\n" |
259 | 0 | "-C Combine mols in first file with others by name\n" |
260 | 0 | "--filter <filterstring> Filter: convert only when tests are true:\n" |
261 | 0 | "--add <list> Add properties from descriptors\n" |
262 | 0 | "--delete <list> Delete properties in list\n" |
263 | 0 | "--append <list> Append properties or descriptors in list to title:\n" |
264 | 0 | "-s\"smarts\" Convert only if match SMARTS or mols in file:\n" |
265 | 0 | "-v\"smarts\" Convert only if NO match to SMARTS or mols in file(not displayed in GUI)\n" |
266 | 0 | "--join Join all input molecules into a single output molecule\n" |
267 | 0 | "--separate Output disconnected fragments separately\n" |
268 | 0 | "--property <attrib> <value> add or replace a property (SDF)\n" |
269 | 0 | "--title <title> Add or replace molecule title\n" |
270 | 0 | "--addtotitle <text> Append text to title\n" |
271 | 0 | "--writeconformers Output multiple conformers separately\n" |
272 | 0 | "--addoutindex Append output index to title\n" ; |
273 | | |
274 | | //Append lines from OBOp plugins that work with OBMol |
275 | 0 | OBMol dummymol; //just needed to carry class type information; messy! |
276 | 0 | ret += OBOp::OpOptions(&dummymol); |
277 | |
|
278 | 0 | return ret.c_str(); |
279 | 0 | } |
280 | | |
281 | | } //namespace OpenBabel |
282 | | |
283 | | //! \file transform.cpp |
284 | | //! \brief Perform command-line requested transformations for OBMol |
285 | | //! and SMARTS filtering |