1 /**********************************************************************
2 Copyright (C) 2002-2006 by Dr. Alex M. Clark and Geoffrey Hutchison
3 Some portions Copyright (C) 2004 by Chris Morley
4 
5 This program 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 version 2 of the License.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13 ***********************************************************************/
14 
15 #include <openbabel/babelconfig.h>
16 #include <openbabel/obmolecformat.h>
17 #include <openbabel/mol.h>
18 #include <openbabel/atom.h>
19 #include <openbabel/bond.h>
20 #include <openbabel/elements.h>
21 #include <cstdlib>
22 
23 #include <sstream>
24 
25 using namespace std;
26 namespace OpenBabel
27 {
28 
29   class CRK2DFormat : public OBMoleculeFormat
30   {
31   public:
32     //Register this format type ID
CRK2DFormat()33     CRK2DFormat()
34     {
35       OBConversion::RegisterFormat("crk2d", this, "chemical/x-crk2d");
36     }
37 
Description()38     virtual const char* Description() //required
39     {
40       return
41         "Chemical Resource Kit diagram(2D)\n"
42         "No comments yet\n";
43     };
44 
SpecificationURL()45     virtual const char* SpecificationURL()
46     {return "http://crk.sourceforge.net/";}; //optional
47 
GetMIMEType()48     virtual const char* GetMIMEType()
49     { return "chemical/x-crk2d"; };
50 
51     //Flags() can return be any the following combined by | or be omitted if none apply
52     // NOTREADABLE  READONEONLY  NOTWRITABLE  WRITEONEONLY
Flags()53     virtual unsigned int Flags()
54     {
55       return READONEONLY;
56     };
57 
58     //*** This section identical for most OBMol conversions ***
59     ////////////////////////////////////////////////////
60     /// The "API" interface functions
61     virtual bool ReadMolecule(OBBase* pOb, OBConversion* pConv);
62     virtual bool WriteMolecule(OBBase* pOb, OBConversion* pConv);
63 
64     static bool ReadCRK(std::istream &ifs,OBMol &mol,const char *classTag);
65     static void WriteCRK(std::ostream &ofs,OBMol &mol,bool GroupCharges);
66 
67   };
68 
69 
70   //Make an instance of the format class
71   CRK2DFormat theCRK2DFormat;
72 
73   /////////////////////////////////////////////////////////////////
ReadMolecule(OBBase * pOb,OBConversion * pConv)74   bool CRK2DFormat::ReadMolecule(OBBase* pOb, OBConversion* pConv)
75   {
76 
77     OBMol* pmol = pOb->CastAndClear<OBMol>();
78     if (pmol == nullptr)
79       return false;
80 
81     //Define some references so we can use the old parameter names
82     istream &ifs = *pConv->GetInStream();
83     OBMol &mol = *pmol;
84     mol.SetTitle( pConv->GetTitle()); //default title is the filename
85 
86     char buffer[BUFF_SIZE];//CM extra buffer
87 
88     if (!ifs.getline(buffer,BUFF_SIZE))
89       {
90         obErrorLog.ThrowError(__FUNCTION__, "File is empty!", obError);
91         return(false);
92       }
93     if (!strstr(buffer,"<Property"))
94       {
95         obErrorLog.ThrowError(__FUNCTION__, "Not valid CRK XML", obWarning);
96         return false;
97       }
98     if (!strstr(buffer,"\"DiagramStructure\""))
99       {
100         obErrorLog.ThrowError(__FUNCTION__,"Not CRK DiagramStructure (2D)", obWarning);
101         return false;
102       }
103 
104     mol.SetDimension(2);
105     return ReadCRK(ifs,mol,"Structure2D");
106   }
107 
108   ////////////////////////////////////////////////////////////////
109 
WriteMolecule(OBBase * pOb,OBConversion * pConv)110   bool CRK2DFormat::WriteMolecule(OBBase* pOb, OBConversion* pConv)
111   {
112     OBMol* pmol = dynamic_cast<OBMol*>(pOb);
113     if (pmol == nullptr)
114       return false;
115 
116     //Define some references so we can use the old parameter names
117     ostream &ofs = *pConv->GetOutStream();
118     OBMol &mol = *pmol;
119 
120     ofs << "<Property Type=\"DiagramStructure\">" <<  endl;
121     ofs << " <Structure2D>" << endl;
122 
123     WriteCRK(ofs,mol,true);
124 
125     ofs << " </Structure2D>" << endl;
126     ofs << "</Property>" << endl;
127 
128     return true;
129   }
130 
131   //******************************************************
132   class CRK3DFormat : public OBMoleculeFormat
133   {
134   public:
135     //Register this format type ID
CRK3DFormat()136     CRK3DFormat()
137     {
138       OBConversion::RegisterFormat("crk3d", this, "chemical/x-crk3d");
139     }
140 
Description()141     virtual const char* Description() //required
142     {
143       return
144         "Chemical Resource Kit 3D format\n"
145         "No comments yet\n";
146     };
147 
SpecificationURL()148     virtual const char* SpecificationURL()
149     {return "http://crk.sourceforge.net/";}; //optional
150 
GetMIMEType()151     virtual const char* GetMIMEType()
152     { return "chemical/x-crk3d"; };
153 
154     //Flags() can return be any the following combined by | or be omitted if none apply
155     // NOTREADABLE  READONEONLY  NOTWRITABLE  WRITEONEONLY
Flags()156     virtual unsigned int Flags()
157     {
158       return READONEONLY;
159     };
160 
161     //*** This section identical for most OBMol conversions ***
162     ////////////////////////////////////////////////////
163     /// The "API" interface functions
164     virtual bool ReadMolecule(OBBase* pOb, OBConversion* pConv);
165     virtual bool WriteMolecule(OBBase* pOb, OBConversion* pConv);
166   };
167   //***
168 
169   //Make an instance of the format class
170   CRK3DFormat theCRK3DFormat;
171 
172   /////////////////////////////////////////////////////////////////
ReadMolecule(OBBase * pOb,OBConversion * pConv)173   bool CRK3DFormat::ReadMolecule(OBBase* pOb, OBConversion* pConv)
174   {
175 
176     OBMol* pmol = pOb->CastAndClear<OBMol>();
177     if (pmol == nullptr)
178       return false;
179 
180     //Define some references so we can use the old parameter names
181     istream &ifs = *pConv->GetInStream();
182     OBMol &mol = *pmol;
183     mol.SetTitle( pConv->GetTitle()); //default title is the filename
184 
185     char buffer[BUFF_SIZE];//CM extra buffer
186 
187     if (!ifs.getline(buffer,BUFF_SIZE))
188       {
189         obErrorLog.ThrowError(__FUNCTION__, "File is empty!", obError);
190         return(false);
191       }
192     if (!strstr(buffer,"<Property"))
193       {
194         obErrorLog.ThrowError(__FUNCTION__, "Not valid CRK XML", obWarning);
195         return false;
196       }
197     if (!strstr(buffer,"\"ModelStructure\"") && !strstr(buffer,"\"XRayStructure\""))
198       {
199         obErrorLog.ThrowError(__FUNCTION__,"Not CRK ModelStructure or XRayStructure (3D).", obWarning);
200         return false;
201       }
202 
203     return CRK2DFormat::ReadCRK(ifs,mol,"Structure3D");
204   }
205 
206   ////////////////////////////////////////////////////////////////
207 
WriteMolecule(OBBase * pOb,OBConversion * pConv)208   bool CRK3DFormat::WriteMolecule(OBBase* pOb, OBConversion* pConv)
209   {
210     OBMol* pmol = dynamic_cast<OBMol*>(pOb);
211     if (pmol == nullptr)
212       return false;
213 
214     //Define some references so we can use the old parameter names
215     ostream &ofs = *pConv->GetOutStream();
216     OBMol &mol = *pmol;
217 
218     ofs << "<Property Type=\"ModelStructure\">" <<  endl;
219     ofs << " <Structure3D>" << endl;
220 
221     CRK2DFormat::WriteCRK(ofs,mol,true);
222 
223     ofs << " </Structure3D>" << endl;
224     ofs << "</Property>" << endl;
225 
226     return true;
227   }
228 
229   //**************************************************************
ReadCRK(std::istream & ifs,OBMol & mol,const char * classTag)230   bool CRK2DFormat::ReadCRK(std::istream &ifs,OBMol &mol,const char *classTag)
231   {
232     bool foundClass=false;
233 
234 #define MAX_ATOMS 1000
235 
236     int numAtoms=0;
237     int statomID[MAX_ATOMS];
238 
239 #define MAX_BONDS 1000
240 
241     int numBonds=0;
242     int stbondFrom[MAX_BONDS],stbondTo[MAX_BONDS],stbondStyle[MAX_BONDS];
243     double stbondOrder[MAX_BONDS];
244 
245     bool inAtom=false,inBond=false;
246     int atomID,atomNumber;
247     double atomX,atomY,atomZ,atomCharge;
248     int bondFrom,bondTo,bondStyle;
249     double bondOrder = 0.0f;
250     char buffer[BUFF_SIZE];//was global
251 
252     mol.BeginModify();
253 
254     while (ifs.getline(buffer,BUFF_SIZE))
255       {
256         if (strstr(buffer,classTag) && foundClass == false)
257           foundClass=true;
258         else if (strstr(buffer,classTag) && foundClass == true)
259           break;
260         else if (strstr(buffer,"<Atom"))
261           {
262             atomID=0;
263             char *tag=strstr(buffer,"ID=\"");
264             if (tag)
265               atomID=atoi(tag+4);
266             if (atomID>0)
267               {
268                 inAtom=true;
269                 atomNumber=0;
270                 atomX= atomY= atomZ= atomCharge =0.0;
271               }
272             else
273               continue; // atomID <= 0
274           }
275         else if (strstr(buffer,"<Bond"))
276           {
277             inBond=true;
278             bondFrom=bondTo=bondStyle=0;
279             bondOrder=0;
280           }
281         else if (strstr(buffer,"</Atom>"))
282           {
283             if (inAtom && numAtoms<MAX_ATOMS)
284               {
285                 OBAtom atm;
286                 atm.Clear();
287 
288                 statomID[numAtoms++]=atomID;
289 
290                 atm.SetAtomicNum(atomNumber);
291                 atm.SetVector(atomX,atomY,atomZ);
292                 atm.SetFormalCharge((int)atomCharge);
293 
294                 if (!mol.AddAtom(atm))
295                   {
296                     obErrorLog.ThrowError(__FUNCTION__, "Unable to add atom.", obWarning);
297                     return false;
298                   }
299               }
300             inAtom=false;
301           }
302         else if (strstr(buffer,"</Bond>"))
303           {
304             if (inBond && numBonds<MAX_BONDS)
305               {
306                 stbondFrom[numBonds]=bondFrom;
307                 stbondTo[numBonds]=bondTo;
308                 stbondOrder[numBonds]=bondOrder;
309                 stbondStyle[numBonds]=bondStyle;
310                 numBonds++;
311               }
312             inBond=false;
313           }
314         else
315           {
316             char *tag;
317             if (inAtom)
318               {
319                 tag=strstr(buffer,"<X>");
320                 if (tag)
321                   atomX=atof(tag+3);
322                 tag=strstr(buffer,"<Y>");
323                 if (tag)
324                   atomY=atof(tag+3);
325                 tag=strstr(buffer,"<Z>");
326                 if (tag)
327                   atomZ=atof(tag+3);
328                 tag=strstr(buffer,"<Element>");
329                 if (tag)
330                   {
331                     char element[3]="\0\0";
332                     element[0]=tag[9];
333                     if (tag[10]>='a' && tag[10]<='z')
334                       element[1]=tag[10];
335                     atomNumber=OBElements::GetAtomicNum(element);
336                   }
337                 tag=strstr(buffer,"<Charge>");
338                 if (tag)
339                   atomCharge=atof(tag+8);
340               }
341             if (inBond)
342               {
343                 tag=strstr(buffer,"<From>");
344                 if (tag)
345                   bondFrom=atoi(tag+6);
346                 tag=strstr(buffer,"<To>");
347                 if (tag)
348                   bondTo=atoi(tag+4);
349                 tag=strstr(buffer,"<Order>");
350                 if (tag)
351                   bondOrder=atof(tag+7);
352                 tag=strstr(buffer,"<Style>");
353                 if (tag)
354                   bondStyle=atoi(tag+7);
355               }
356           }
357       }
358 
359     for(int n=0;n<numBonds;n++)
360       {
361         int fromIdx=0,toIdx=0;
362         for(int i=0;i<numAtoms;i++)
363           {
364             if (stbondFrom[n]==statomID[i])
365               fromIdx=i+1;
366             if (stbondTo[n]==statomID[i])
367               toIdx=i+1;
368           }
369 
370         if (fromIdx>0 && toIdx>0)
371           {
372             OBAtom *from=mol.GetAtom(fromIdx),*to=mol.GetAtom(toIdx);
373 
374             int order=1;
375             if (stbondOrder[n]==2)
376               order=2;
377             else if (stbondOrder[n]==3)
378               order=3;
379             else if (stbondOrder[n]==1.5)
380               order=5;
381 
382             OBBond bnd;
383             bnd.Set(n+1,from,to,order,0);
384 
385             if (stbondStyle[n]==1)
386               bnd.SetWedge();
387             if (stbondStyle[n]==2)
388               bnd.SetHash();
389             if (stbondOrder[n]==1.5)
390               bnd.SetAromatic();
391 
392             if (!mol.AddBond(bnd))
393               {
394                 obErrorLog.ThrowError(__FUNCTION__, "Unable to add bond.", obWarning);
395                 return false;
396               }
397           }
398         else
399           {
400             stringstream errorMsg;
401             errorMsg << "Unassigned bond ID (" << stbondFrom[n]
402                      << " " << stbondTo[n] << "), source may be invalid.";
403             obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning);
404             return false;
405           }
406       }
407 
408     mol.EndModify();
409 
410     // we likely have an </Property> line to gobble up
411     if (ifs.peek() != EOF && ifs.good())
412       {
413         ifs.getline(buffer,BUFF_SIZE);
414         if (strstr(buffer, "</Property>") == nullptr)
415           return false; // something messed up
416       }
417 
418     return foundClass;
419   }
420 
WriteCRK(std::ostream & ofs,OBMol & mol,bool GroupCharges)421   void CRK2DFormat::WriteCRK(std::ostream &ofs,OBMol &mol,bool GroupCharges)
422   {
423     double groupCharge=0;
424     if (GroupCharges)
425       for(unsigned int n=1;n<=mol.NumAtoms();n++)
426         groupCharge+=mol.GetAtom(n)->GetFormalCharge();
427 
428     ofs << "  <Group Charge=\"" << groupCharge << "\" Spin=\"0\">" << endl;
429 
430     for(unsigned int n=1;n<=mol.NumAtoms();n++)
431       {
432         OBAtom *atm=mol.GetAtom(n);
433 
434         int id=atm->GetIdx(),atomnum=atm->GetAtomicNum();
435         double x=atm->GetX(),y=atm->GetY(),z=atm->GetZ();
436         const char *element=OBElements::GetSymbol(atomnum);
437         double charge=0;
438         if (!GroupCharges)
439           charge=atm->GetFormalCharge();
440 
441         //ofs << ((int)atm) << endl;
442 
443         ofs << "   <Atom ID=\"" << id << "\">" << endl;
444         ofs << "    <X>" << x << "</X>" << endl;
445         ofs << "    <Y>" << y << "</Y>" << endl;
446         ofs << "    <Z>" << z << "</Z>" << endl;
447         ofs << "    <Element>" << element << "</Element>" << endl;
448         if (charge!=0)
449           ofs << "    <Charge>" << charge << "</Charge>" << endl;
450         ofs << "   </Atom>" << endl;
451       }
452 
453     for(unsigned int m=0;m<mol.NumBonds();m++)
454       {
455         OBBond *bnd=mol.GetBond(m);
456 
457         int from=bnd->GetBeginAtom()->GetIdx(),to=bnd->GetEndAtom()->GetIdx();
458         double order=bnd->GetBondOrder();
459         if (bnd->IsAromatic())
460           order=1.5;
461         int style=0;
462         if (bnd->IsHash())
463           style=1;
464         if (bnd->IsWedge())
465           style=2;
466 
467         ofs << "   <Bond>" << endl;
468         ofs << "    <From>" << from << "</From>" << endl;
469         ofs << "    <To>" << to << "</To>" << endl;
470         ofs << "    <Order>" << order << "</Order>" << endl;
471         ofs << "    <Style>" << style << "</Style>" << endl;
472         ofs << "   </Bond>" << endl;
473       }
474 
475     ofs << "  </Group>" << endl;
476   }
477 
478 } //namespace OpenBabel
479