1 // FILETRANS.CPP : Handle import/export through use of the Babel external lib.
2 
3 // Copyright (C) 2000 Geoffrey Hutchison.
4 
5 // This package is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 
10 // This package is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 
15 // You should have received a copy of the GNU General Public License
16 // along with this package; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 
19 /*################################################################################################*/
20 
21 #include "filetrans.h"
22 
23 #ifdef ENABLE_OPENBABEL
24 
25 #include <ghemical/libghemicaldefine.h>
26 
27 #include "project.h"
28 
29 #include <fstream>
30 #include <sstream>
31 using namespace std;
32 
33 // Babel lib includes
34 // ^^^^^^^^^^^^^^^^^^
35 
36 #include <openbabel/mol.h>
37 #include <openbabel/obutil.h>
38 #include <openbabel/data.h>
39 #include <openbabel/typer.h>
40 #include <openbabel/obconversion.h>
41 #include <openbabel/atom.h>
42 #include <openbabel/bond.h>
43 
44 using namespace std;
45 using namespace OpenBabel;
46 
47 #ifndef FORMAT_PATH
48 #define FORMAT_PATH (char *) project::appdata_path
49 #endif	// FORMAT_PATH
50 
51 #if 0
52 namespace OpenBabel
53 {
54 	extern OBAromaticTyper aromtyper;
55 	extern OBAtomTyper atomtyper;
56 	extern OBElementTable etab;
57 	extern OBTypeTable ettab;
58 	extern OBChainsParser chainsparser;
59 	extern OBIsotopeTable isotab;
60 }
61 #endif
62 
63 /*################################################################################################*/
64 
65 // Constructor
66 // Requires: None
67 // Provides: import, export vectors for future use
file_trans()68 file_trans::file_trans()
69 {
70 	format_record current;
71 
72 // it seems that under Open Babel these objects (aromtyper, atomtyper,
73 // extab, etab, ttab) are global, so here we could initialize them
74 // multiple times is case we create multiple file_trans objects.
75 // could this be risky and/or problematic??? 2001-05-28 TH
76 
77 // Certainly not risky -- the objects track whether they have been
78 // initialized. It may or may not be problematic in terms of running
79 // the library multiple times, but debugging should show that.
80 
81 // But this actually shouldn't be necessary -- since Ghemical isn't
82 // building and installing Open Babel itself. Instead, let the library
83 // find its own data files! -GRH 2005-09-27
84 
85 //aromtyper.SetReadDirectory(FORMAT_PATH);		// aromatic typer
86 //aromtyper.SetEnvironmentVariable("GHEMICAL_DIR");
87 //atomtyper.SetReadDirectory(FORMAT_PATH);		// atom typer
88 //atomtyper.SetEnvironmentVariable("GHEMICAL_DIR");
89 //etab.SetReadDirectory(FORMAT_PATH);
90 //etab.SetEnvironmentVariable("GHEMICAL_DIR");
91 //ttab.SetReadDirectory(FORMAT_PATH);
92 //ttab.SetEnvironmentVariable("GHEMICAL_DIR");
93 //isotab.SetReadDirectory(FORMAT_PATH);
94 //isotab.SetEnvironmentVariable("GHEMICAL_DIR");
95 
96 	OBConversion conv;
97 	Formatpos pos;
98 	OBFormat * pFormat;
99 	const char * str = NULL;
100 	while(OBConversion::GetNextFormat(pos,str,pFormat))
101 	{
102 		if ((pFormat->Flags() & NOTWRITABLE) && (pFormat->Flags() & NOTREADABLE)) continue;
103 
104 		current.format = pFormat;
105 		std::string tempDescription(pFormat->Description());
106 
107 		current.description = tempDescription.substr(0, tempDescription.find('\n'));
108 
109 		if ( !(pFormat->Flags() & NOTREADABLE) ) imports.push_back(current);
110 		if ( !(pFormat->Flags() & NOTWRITABLE) ) exports.push_back(current);
111 	}
112 
113 	// the rest is for compatibility part...
114 	// the rest is for compatibility part...
115 	// the rest is for compatibility part...
116 
117 	obm = NULL;
118 	prj = NULL;
119 
120 	name_tag_count = -1;
121 	tagtab = NULL;
122 }
123 
~file_trans()124 file_trans::~file_trans()
125 {
126 	// we will delete the OBMol and name tags!!!
127 
128 	if (obm != NULL) delete obm;
129 	if (tagtab != NULL) delete[] tagtab;
130 }
131 
132 // Import
133 // Requires: filename (for typing) and an istream to read
134 // Provides:
Import(const char * filename,istream & input,ostream & output)135 int file_trans::Import(const char *filename, istream &input, ostream &output)
136 {
137 	OBConversion conv;
138 	OBFormat * inFormat = conv.FormatFromExt(filename);
139 	if (inFormat == NULL) return -1; // cannot find that format
140 
141 	OBFormat * outFormat = conv.FindFormat("gpr"); // GHEMICAL
142 
143 	if (! conv.SetInAndOutFormats(inFormat, outFormat) ) return -1;	// cannot read/write these formats
144 
145 	conv.Convert(&input, &output);
146 	return(0);
147 }
148 
Export(const char * filename,istream & input,ostream & output)149 int file_trans::Export(const char *filename, istream &input, ostream &output)
150 {
151 	OBConversion conv;
152 	OBFormat * inFormat = conv.FindFormat("gpr"); // GHEMICAL
153 	OBFormat * outFormat = conv.FormatFromExt(filename);
154 
155 	if (outFormat == NULL) return -1; // cannot write that format
156 
157 	if (! conv.SetInAndOutFormats(inFormat, outFormat) ) return -1;	// cannot read/write these formats
158 
159 	conv.Convert(&input, &output);
160 	return(0);
161 }
162 
CanImport(const char * filename)163 bool file_trans::CanImport(const char *filename)
164 {
165 	OBConversion conv;
166 	OBFormat * pFormat = conv.FormatFromExt(filename);
167 	bool canRead = ( pFormat && !(pFormat->Flags() & NOTREADABLE) );
168 	return canRead;
169 }
170 
CanExport(const char * filename)171 bool file_trans::CanExport(const char *filename)
172 {
173 	OBConversion conv;
174 	OBFormat * pFormat = conv.FormatFromExt(filename);
175 	bool canWrite = ( pFormat && !(pFormat->Flags() & NOTWRITABLE) );
176 	return canWrite;
177 }
178 
GetExportDescription(unsigned int index)179 string file_trans::GetExportDescription(unsigned int index)
180 {
181 	string temp;
182 	if (index < exports.size())
183 	temp = exports[index].description;
184 	return temp;
185 }
186 
GetImportDescription(unsigned int index)187 string file_trans::GetImportDescription(unsigned int index)
188 {
189 	string temp;
190 	if (index < imports.size())
191 	temp = imports[index].description;
192 	return temp;
193 }
194 
195 // Import
196 // Requires: filename, a type and an istream to read
197 // Provides: an imported file using Babel
Import(const char * filename,unsigned int type,istream & input,ostream & output)198 int file_trans::Import(const char *filename, unsigned int type, istream &input, ostream &output)
199 {
200 	OBConversion conv;
201 	OBFormat * inFormat;
202 
203 	if (type < imports.size()) inFormat = imports[type].format;
204 	else
205 	{
206 		inFormat = conv.FormatFromExt(filename);
207 		if (inFormat == NULL) return -1; // cannot find that format
208 	}
209 
210 	OBFormat * outFormat = conv.FindFormat("gpr"); // GHEMICAL
211 
212 	if (! conv.SetInAndOutFormats(inFormat, outFormat) ) return -1; // cannot read/write these formats
213 
214 	conv.Convert(&input, &output);
215 	return(0);
216 }
217 
218 // Export
219 // Requires: filename, a type and an istream to read
220 // Provides: an exported file using Babel
Export(const char * filename,unsigned int type,istream & input,ostream & output)221 int file_trans::Export(const char *filename, unsigned int type, istream &input, ostream &output)
222 {
223 	OBConversion conv;
224 	OBFormat * outFormat;
225 
226 	if (type < exports.size()) outFormat = exports[type].format;
227 	else
228 	{
229 		OBFormat * outFormat = conv.FormatFromExt(filename);
230 		if (outFormat == NULL) return -1; // cannot write that format
231 	}
232 
233 	OBFormat * inFormat = conv.FindFormat("gpr"); // GHEMICAL
234 
235 	if (! conv.SetInAndOutFormats(inFormat, outFormat) ) return -1;	// cannot read/write these formats
236 
237 	conv.Convert(&input, &output);
238 	return(0);
239 }
240 
241 /*################*/
242 /*################*/
243 
Copy(project * p1,iter_al p2,iter_al p3)244 OBMol * file_trans::Copy(project * p1, iter_al p2, iter_al p3)
245 {
246 	prj = p1; obm = new OBMol(); itb = p2; ite = p3;
247 
248 	// count the atoms and reserve memory for the name tags.
249 	// count the atoms and reserve memory for the name tags.
250 	// count the atoms and reserve memory for the name tags.
251 
252 	name_tag_count = 0;
253 	for (iter_al it1 = itb;it1 != ite;it1++)
254 	{
255 		name_tag_count++;
256 	}
257 
258 	tagtab = new atom_name_tag[name_tag_count];
259 
260 	// copy the atoms/bonds to OBMol...
261 
262 	obm->BeginModify();
263 
264 	// copy the atoms.
265 
266 	int tag_counter = 0;
267 	for (iter_al it1 = itb;it1 != ite;it1++)
268 	{
269 		OBAtom * oba = obm->NewAtom();
270 		oba->SetAtomicNum((* it1).el.GetAtomicNumber());
271 
272 		const fGL * cdata = (* it1).GetCRD(0);
273 		float xcrd = cdata[0] * 10.0;
274 		float ycrd = cdata[1] * 10.0;
275 		float zcrd = cdata[2] * 10.0;
276 
277 		oba->SetVector(xcrd, ycrd, zcrd);
278 
279 		// now set the "name tag" for this atom...
280 		// now set the "name tag" for this atom...
281 		// now set the "name tag" for this atom...
282 
283 		tagtab[tag_counter].atmr = & (* it1);
284 		tagtab[tag_counter].oba = oba;
285 		tag_counter++;
286 	}
287 
288 	// copy the bonds.
289 
290 // WE ASSUME HERE THAT ATOM ITERATORS COVER THE WHOLE SYSTEM!!!
291 // WE ASSUME HERE THAT ATOM ITERATORS COVER THE WHOLE SYSTEM!!!
292 // WE ASSUME HERE THAT ATOM ITERATORS COVER THE WHOLE SYSTEM!!!
293 prj->UpdateIndex();
294 
295 	for (iter_bl it1 = prj->GetBondsBegin();it1 != prj->GetBondsEnd();it1++)
296 	{
297 		i32s ind1 = (* it1).atmr[0]->index + 1;
298 		i32s ind2 = (* it1).atmr[1]->index + 1;
299 
300 		i32s bt;
301 		switch ((* it1).bt.GetValue())
302 		{
303 			case BONDTYPE_SINGLE:	bt = 1; break;
304 			case BONDTYPE_DOUBLE:	bt = 2; break;
305 			case BONDTYPE_TRIPLE:	bt = 3; break;
306 			case BONDTYPE_CNJGTD:	bt = 5; break;
307 			default:		bt = 1;
308 		}
309 
310 		obm->AddBond(ind1, ind2, bt);
311 	}
312 
313 	// ok, it's ready!
314 
315 	obm->EndModify();
316 	return obm;
317 }
318 
CopyMolecule(project *,int)319 OBMol * file_trans::CopyMolecule(project *, int)
320 {
321 	return NULL;
322 }
323 
CopyAll(project * prj)324 OBMol * file_trans::CopyAll(project * prj)
325 {
326 	return Copy(prj, prj->GetAtomsBegin(), prj->GetAtomsEnd());
327 }
328 
Synchronize(void)329 void file_trans::Synchronize(void)
330 {
331 	// create a new tagtab for the current situation (for bonds)...
332 
333 	atom_name_tag * tagtab2 = new atom_name_tag[obm->NumAtoms()];
334 
335 	for (i32u n1 = 1;n1 <= obm->NumAtoms();n1++)
336 	{
337 		OBAtom * oba = obm->GetAtom(n1);
338 		atom * atmr = NULL;
339 
340 		// try to find the matching name tag; if you find it, get it and remove the original.
341 
342 		for (i32s n2 = 0;n2 < name_tag_count;n2++)
343 		{
344 			if (tagtab[n2].oba != oba) continue;
345 
346 			// match found!!!
347 			// match found!!!
348 			// match found!!!
349 
350 			atmr = tagtab[n2].atmr;
351 			tagtab[n2].atmr = NULL;
352 			tagtab[n2].oba = NULL;
353 			break;
354 		}
355 
356 		// if atmr is still NULL, we did not have a matching tag -> this must be a new atom!
357 		// otherwise, we have this pair of corresponding objects and we can synchronize.
358 
359 		if (!atmr)
360 		{
361 			element el(oba->GetAtomicNum());
362 
363 			fGL crd[3] =
364 			{
365 				oba->GetX() / 10.0,
366 				oba->GetY() / 10.0,
367 				oba->GetZ() / 10.0
368 			};
369 
370 			atom newatom(el, crd, prj->GetCRDSetCount());
371 			prj->AddAtom_lg(newatom); atmr = & prj->GetLastAtom();
372 
373 	// this seems to crash -> disabled!!!!!!!!!!
374 	//		atmr->charge = oba->GetPartialCharge();
375 		}
376 		else
377 		{
378 			atmr->el = element(oba->GetAtomicNum());
379 			atmr->SetCRD(0, oba->GetX() / 10.0, oba->GetY() / 10.0, oba->GetZ() / 10.0);
380 
381 	// this seems to crash -> disabled!!!!!!!!!!
382 	//		atmr->charge = oba->GetPartialCharge();
383 		}
384 
385 		// save the new tagtab entry...
386 
387 		tagtab2[n1 - 1].atmr = atmr;
388 		tagtab2[n1 - 1].oba = oba;
389 	}
390 
391 	// in the above loop, we discarded the name tags that had corresponding atoms.
392 	// so if there still are name tags left, it means we should remove those atoms!
393 	// removing atoms will also remove bonds connected to them automagically...
394 
395 	for (i32s n1 = 0;n1 < name_tag_count;n1++)
396 	{
397 		if (!tagtab[n1].atmr) continue;
398 
399 		// ok, remove this atom.
400 		// ok, remove this atom.
401 		// ok, remove this atom.
402 
403 		iter_al it1 = itb;
404 		while (it1 != ite) if (& (* it1) == tagtab[n1].atmr) break;
405 		if (it1 == itb)
406 		{
407 			assertion_failed(__FILE__, __LINE__, "iterator lost!");
408 		}
409 
410 		tagtab[n1].atmr = NULL;
411 		tagtab[n1].oba = NULL;
412 
413 		prj->RemoveAtom(it1);
414 	}
415 
416 	// now we should have the atoms in sync; do the same for bonds.
417 	// for each OBBond, find or create the equivalent, and check the type.
418 	// we assume that for a pair of atoms, there is no more than 1 bond!!!
419 
420 	for (i32u n1 = 0;n1 < obm->NumBonds();n1++)
421 	{
422 		OBBond * obb = obm->GetBond(n1);
423 		i32u ind1 = obb->GetBeginAtomIdx() - 1;
424 		i32u ind2 = obb->GetEndAtomIdx() - 1;
425 
426 		atom * atmr1 = tagtab2[ind1].atmr;
427 		atom * atmr2 = tagtab2[ind2].atmr;
428 
429 		bond * bndr = NULL;
430 
431 		for (iter_bl it1 = prj->GetBondsBegin();it1 != prj->GetBondsEnd();it1++)
432 		{
433 			bool match = false;
434 			if ((* it1).atmr[0] == atmr1 && (* it1).atmr[1] == atmr2) match = true;
435 			if ((* it1).atmr[1] == atmr1 && (* it1).atmr[0] == atmr2) match = true;
436 			if (!match) continue;
437 
438 			// match found; mark it and check the bondtype.
439 			// match found; mark it and check the bondtype.
440 			// match found; mark it and check the bondtype.
441 
442 			bndr = & (* it1);
443 
444 			i32s bt;
445 			switch (obb->GetBO())
446 			{
447 				case 1:		bt = BONDTYPE_SINGLE; break;
448 				case 2:		bt = BONDTYPE_DOUBLE; break;
449 				case 3:		bt = BONDTYPE_TRIPLE; break;
450 				default:	bt = BONDTYPE_CNJGTD;
451 			}
452 
453 			if (obb->IsAromatic())	bt = BONDTYPE_CNJGTD;
454 
455 			bndr->bt = bondtype(bt);
456 
457 			break;
458 		}
459 
460 		// if there were no match, then create the bond!
461 
462 		if (!bndr)
463 		{
464 			i32s bt;
465 			switch (obb->GetBO())
466 			{
467 				case 1:		bt = BONDTYPE_SINGLE; break;
468 				case 2:		bt = BONDTYPE_DOUBLE; break;
469 				case 3:		bt = BONDTYPE_TRIPLE; break;
470 				default:	bt = BONDTYPE_CNJGTD;
471 			}
472 
473 			if (obb->IsAromatic())	bt = BONDTYPE_CNJGTD;
474 
475 			bond newbond(atmr1, atmr2, bondtype(bt));
476 			prj->AddBond(newbond);
477 		}
478 	}
479 
480 	// get rid of the new tagtab...
481 
482 	delete[] tagtab2;
483 }
484 
485 #endif	// ENABLE_OPENBABEL
486 
487 /*################################################################################################*/
488 
489 // eof
490