JAL-1517 fix copyright for 2.8.2
[jalview.git] / src / MCview / PDBChain.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package MCview;
22
23 import java.util.*;
24
25 import java.awt.*;
26
27 import jalview.analysis.*;
28 import jalview.datamodel.*;
29 import jalview.schemes.*;
30 import jalview.structure.StructureMapping;
31
32 public class PDBChain
33 {
34   /**
35    * SequenceFeature group for PDB File features added to sequences
36    */
37   private static final String PDBFILEFEATURE = "PDBFile";
38
39   private static final String IEASTATUS = "IEA:jalview";
40
41   public String id;
42
43   public Vector bonds = new Vector();
44
45   public Vector atoms = new Vector();
46
47   public Vector residues = new Vector();
48
49   public int offset;
50
51   public Sequence sequence;
52
53   public boolean isNa = false;
54
55   public boolean isVisible = true;
56
57   public int pdbstart = 0;
58
59   public int pdbend = 0;
60
61   public int seqstart = 0;
62
63   public int seqend = 0;
64
65   public String pdbid = "";
66
67   public PDBChain(String pdbid, String id)
68   {
69     this.pdbid = pdbid.toLowerCase();
70     this.id = id;
71   }
72
73   /**
74    * character used to write newlines
75    */
76   protected String newline = System.getProperty("line.separator");
77
78   public void setNewlineString(String nl)
79   {
80     newline = nl;
81   }
82
83   public String getNewlineString()
84   {
85     return newline;
86   }
87
88   public String print()
89   {
90     String tmp = "";
91
92     for (int i = 0; i < bonds.size(); i++)
93     {
94       tmp = tmp + ((Bond) bonds.elementAt(i)).at1.resName + " "
95               + ((Bond) bonds.elementAt(i)).at1.resNumber + " " + offset
96               + newline;
97     }
98
99     return tmp;
100   }
101
102   /**
103    * Annotate the residues with their corresponding positions in s1 using the
104    * alignment in as NOTE: This clears all atom.alignmentMapping values on the
105    * structure.
106    * 
107    * @param as
108    * @param s1
109    */
110   public void makeExactMapping(AlignSeq as, SequenceI s1)
111   {
112     int pdbpos = as.getSeq2Start() - 2;
113     int alignpos = s1.getStart() + as.getSeq1Start() - 3;
114     // first clear out any old alignmentMapping values:
115     for (Atom atom : (Vector<Atom>) atoms)
116     {
117       atom.alignmentMapping = -1;
118     }
119     // and now trace the alignment onto the atom set.
120     for (int i = 0; i < as.astr1.length(); i++)
121     {
122       if (as.astr1.charAt(i) != '-')
123       {
124         alignpos++;
125       }
126
127       if (as.astr2.charAt(i) != '-')
128       {
129         pdbpos++;
130       }
131
132       if (as.astr1.charAt(i) == as.astr2.charAt(i))
133       {
134         Residue res = (Residue) residues.elementAt(pdbpos);
135         Enumeration en = res.atoms.elements();
136         while (en.hasMoreElements())
137         {
138           Atom atom = (Atom) en.nextElement();
139           atom.alignmentMapping = alignpos;
140         }
141       }
142     }
143   }
144
145   /**
146    * copy over the RESNUM seqfeatures from the internal chain sequence to the
147    * mapped sequence
148    * 
149    * @param seq
150    * @param status
151    *          The Status of the transferred annotation
152    * @return the features added to sq (or its dataset)
153    */
154   public SequenceFeature[] transferRESNUMFeatures(SequenceI seq,
155           String status)
156   {
157     SequenceI sq = seq;
158     while (sq != null && sq.getDatasetSequence() != null)
159     {
160       sq = sq.getDatasetSequence();
161       if (sq == sequence)
162       {
163         return null;
164       }
165     }
166     /**
167      * Remove any existing features for this chain if they exist ?
168      * SequenceFeature[] seqsfeatures=seq.getSequenceFeatures(); int
169      * totfeat=seqsfeatures.length; // Remove any features for this exact chain
170      * ? for (int i=0; i<seqsfeatures.length; i++) { }
171      */
172     if (status == null)
173     {
174       status = PDBChain.IEASTATUS;
175     }
176     SequenceFeature[] features = sequence.getSequenceFeatures();
177     for (int i = 0; i < features.length; i++)
178     {
179       if (features[i].getFeatureGroup().equals(pdbid))
180       {
181         SequenceFeature tx = new SequenceFeature(features[i]);
182         tx.setBegin(1 + ((Atom) ((Residue) residues.elementAt(tx.getBegin()
183                 - offset)).atoms.elementAt(0)).alignmentMapping);
184         tx.setEnd(1 + ((Atom) ((Residue) residues.elementAt(tx.getEnd()
185                 - offset)).atoms.elementAt(0)).alignmentMapping);
186         tx.setStatus(status
187                 + ((tx.getStatus() == null || tx.getStatus().length() == 0) ? ""
188                         : ":" + tx.getStatus()));
189         if (tx.begin != 0 && tx.end != 0)
190           sq.addSequenceFeature(tx);
191       }
192     }
193     return features;
194   }
195
196   public void makeCaBondList()
197   {
198     boolean na = false;
199     int numNa = 0;
200     for (int i = 0; i < (residues.size() - 1); i++)
201     {
202       Residue tmpres = (Residue) residues.elementAt(i);
203       Residue tmpres2 = (Residue) residues.elementAt(i + 1);
204       Atom at1 = tmpres.findAtom("CA");
205       Atom at2 = tmpres2.findAtom("CA");
206       na = false;
207       if ((at1 == null) && (at2 == null))
208       {
209         na = true;
210         at1 = tmpres.findAtom("P");
211         at2 = tmpres2.findAtom("P");
212       }
213       if ((at1 != null) && (at2 != null))
214       {
215         if (at1.chain.equals(at2.chain))
216         {
217           if (na)
218           {
219             numNa++;
220           }
221           makeBond(at1, at2);
222         }
223       }
224       else
225       {
226         System.out.println("not found " + i);
227       }
228     }
229     if (numNa > 0 && ((numNa / residues.size()) > 0.99))
230     {
231       isNa = true;
232     }
233   }
234
235   public void makeBond(Atom at1, Atom at2)
236   {
237     float[] start = new float[3];
238     float[] end = new float[3];
239
240     start[0] = at1.x;
241     start[1] = at1.y;
242     start[2] = at1.z;
243
244     end[0] = at2.x;
245     end[1] = at2.y;
246     end[2] = at2.z;
247
248     bonds.addElement(new Bond(start, end, at1, at2));
249   }
250
251   public void makeResidueList()
252   {
253     int count = 0;
254     Object symbol;
255     boolean deoxyn = false;
256     boolean nucleotide = false;
257     StringBuffer seq = new StringBuffer();
258     Vector resFeatures = new Vector();
259     Vector resAnnotation = new Vector();
260     int i, iSize = atoms.size() - 1;
261     int resNumber = -1;
262     for (i = 0; i <= iSize; i++)
263     {
264       Atom tmp = (Atom) atoms.elementAt(i);
265       resNumber = tmp.resNumber;
266       int res = resNumber;
267
268       if (i == 0)
269       {
270         offset = resNumber;
271       }
272
273       Vector resAtoms = new Vector();
274       // Add atoms to a vector while the residue number
275       // remains the same as the first atom's resNumber (res)
276       while ((resNumber == res) && (i < atoms.size()))
277       {
278         resAtoms.addElement((Atom) atoms.elementAt(i));
279         i++;
280
281         if (i < atoms.size())
282         {
283           resNumber = ((Atom) atoms.elementAt(i)).resNumber;
284         }
285         else
286         {
287           resNumber++;
288         }
289       }
290
291       // We need this to keep in step with the outer for i = loop
292       i--;
293
294       // Make a new Residue object with the new atoms vector
295       residues.addElement(new Residue(resAtoms, resNumber - 1, count));
296
297       Residue tmpres = (Residue) residues.lastElement();
298       Atom tmpat = (Atom) tmpres.atoms.elementAt(0);
299       // Make A new SequenceFeature for the current residue numbering
300       SequenceFeature sf = new SequenceFeature("RESNUM", tmpat.resName
301               + ":" + tmpat.resNumIns + " " + pdbid + id, "", offset
302               + count, offset + count, pdbid);
303       // MCview.PDBChain.PDBFILEFEATURE);
304       resFeatures.addElement(sf);
305       resAnnotation.addElement(new Annotation(tmpat.tfactor));
306       // Keep totting up the sequence
307       if ((symbol = ResidueProperties.getAA3Hash().get(tmpat.resName)) == null)
308       {
309         String nucname = tmpat.resName.trim();
310         // use the aaIndex rather than call 'toLower' - which would take a bit
311         // more time.
312         deoxyn = nucname.length() == 2
313                 && ResidueProperties.aaIndex[nucname.charAt(0)] == ResidueProperties.aaIndex['D'];
314         if (tmpat.name.equalsIgnoreCase("CA")
315                 || ResidueProperties.nucleotideIndex[nucname
316                         .charAt((deoxyn ? 1 : 0))] == -1)
317         {
318           seq.append("X");
319           // System.err.println("PDBReader:Null aa3Hash for " +
320           // tmpat.resName);
321         }
322         else
323         {
324           // nucleotide flag
325           nucleotide = true;
326           seq.append(nucname.charAt((deoxyn ? 1 : 0)));
327         }
328       }
329       else
330       {
331         if (nucleotide)
332         {
333           System.err
334                   .println("Warning: mixed nucleotide and amino acid chain.. its gonna do bad things to you!");
335         }
336         seq.append(ResidueProperties.aa[((Integer) symbol).intValue()]);
337       }
338       count++;
339     }
340
341     if (id.length() < 1)
342     {
343       id = " ";
344     }
345     isNa = nucleotide;
346     sequence = new Sequence(id, seq.toString(), offset, resNumber - 1); // Note:
347     // resNumber-offset
348     // ~=
349     // seq.size()
350     // Add normalised feature scores to RESNUM indicating start/end of sequence
351     // sf.setScore(offset+count);
352
353     // System.out.println("PDB Sequence is :\nSequence = " + seq);
354     // System.out.println("No of residues = " + residues.size());
355     for (i = 0, iSize = resFeatures.size(); i < iSize; i++)
356     {
357       sequence.addSequenceFeature((SequenceFeature) resFeatures
358               .elementAt(i));
359       resFeatures.setElementAt(null, i);
360     }
361     Annotation[] annots = new Annotation[resAnnotation.size()];
362     float max = 0;
363     for (i = 0, iSize = annots.length; i < iSize; i++)
364     {
365       annots[i] = (Annotation) resAnnotation.elementAt(i);
366       if (annots[i].value > max)
367         max = annots[i].value;
368       resAnnotation.setElementAt(null, i);
369     }
370     AlignmentAnnotation tfactorann = new AlignmentAnnotation(
371             "PDB.TempFactor", "Temperature Factor for "
372                     + sequence.getName(), annots, 0, max,
373             AlignmentAnnotation.LINE_GRAPH);
374     tfactorann.setSequenceRef(sequence);
375     sequence.addAlignmentAnnotation(tfactorann);
376   }
377
378   public void setChargeColours()
379   {
380     for (int i = 0; i < bonds.size(); i++)
381     {
382       try
383       {
384         Bond b = (Bond) bonds.elementAt(i);
385
386         if (b.at1.resName.equalsIgnoreCase("ASP")
387                 || b.at1.resName.equalsIgnoreCase("GLU"))
388         {
389           b.startCol = Color.red;
390         }
391         else if (b.at1.resName.equalsIgnoreCase("LYS")
392                 || b.at1.resName.equalsIgnoreCase("ARG"))
393         {
394           b.startCol = Color.blue;
395         }
396         else if (b.at1.resName.equalsIgnoreCase("CYS"))
397         {
398           b.startCol = Color.yellow;
399         }
400         else
401         {
402           b.startCol = Color.lightGray;
403         }
404
405         if (b.at2.resName.equalsIgnoreCase("ASP")
406                 || b.at2.resName.equalsIgnoreCase("GLU"))
407         {
408           b.endCol = Color.red;
409         }
410         else if (b.at2.resName.equalsIgnoreCase("LYS")
411                 || b.at2.resName.equalsIgnoreCase("ARG"))
412         {
413           b.endCol = Color.blue;
414         }
415         else if (b.at2.resName.equalsIgnoreCase("CYS"))
416         {
417           b.endCol = Color.yellow;
418         }
419         else
420         {
421           b.endCol = Color.lightGray;
422         }
423       } catch (Exception e)
424       {
425         Bond b = (Bond) bonds.elementAt(i);
426         b.startCol = Color.gray;
427         b.endCol = Color.gray;
428       }
429     }
430   }
431
432   public void setChainColours(jalview.schemes.ColourSchemeI cs)
433   {
434     Bond b;
435     int index;
436     for (int i = 0; i < bonds.size(); i++)
437     {
438       try
439       {
440         b = (Bond) bonds.elementAt(i);
441
442         index = ((Integer) ResidueProperties.aa3Hash.get(b.at1.resName))
443                 .intValue();
444         b.startCol = cs.findColour(ResidueProperties.aa[index].charAt(0));
445
446         index = ((Integer) ResidueProperties.aa3Hash.get(b.at2.resName))
447                 .intValue();
448         b.endCol = cs.findColour(ResidueProperties.aa[index].charAt(0));
449
450       } catch (Exception e)
451       {
452         b = (Bond) bonds.elementAt(i);
453         b.startCol = Color.gray;
454         b.endCol = Color.gray;
455       }
456     }
457   }
458
459   public void setChainColours(Color col)
460   {
461     for (int i = 0; i < bonds.size(); i++)
462     {
463       Bond tmp = (Bond) bonds.elementAt(i);
464       tmp.startCol = col;
465       tmp.endCol = col;
466     }
467   }
468
469   public AlignmentAnnotation[] transferResidueAnnotation(SequenceI seq,
470           String status)
471   {
472     AlignmentAnnotation[] transferred = null;
473
474     return transferred;
475
476   }
477
478   /**
479    * copy any sequence annotation onto the sequence mapped using the provided
480    * StructureMapping
481    * 
482    * @param mapping
483    */
484   public void transferResidueAnnotation(StructureMapping mapping)
485   {
486     SequenceI sq = mapping.getSequence();
487     if (sq != null)
488     {
489       if (sequence != null && sequence.getAnnotation() != null)
490       {
491
492       }
493       float min = -1, max = 0;
494       Annotation[] an = new Annotation[sq.getEnd() - sq.getStart() + 1];
495       for (int i = sq.getStart(), j = sq.getEnd(), k = 0; i <= j; i++, k++)
496       {
497         int prn = mapping.getPDBResNum(k + 1);
498
499         an[k] = new Annotation((float) prn);
500         if (min == -1)
501         {
502           min = k;
503           max = k;
504         }
505         else
506         {
507           if (min > k)
508           {
509             min = k;
510           }
511           else if (max < k)
512           {
513             max = k;
514           }
515         }
516       }
517       sq.addAlignmentAnnotation(new AlignmentAnnotation("PDB.RESNUM",
518               "PDB Residue Numbering for " + this.pdbid + ":" + this.id,
519               an, (float) min, (float) max, AlignmentAnnotation.LINE_GRAPH));
520     }
521   }
522 }