Merge branch 'develop' into feature/JAL-2422ChimeraX
[jalview.git] / src / jalview / structure / AtomSpec.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ 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 jalview.structure;
22
23 /**
24  * Java bean representing an atom in a PDB (or similar) structure model or
25  * viewer
26  * 
27  * @author gmcarstairs
28  *
29  */
30 public class AtomSpec
31 {
32   int modelNo;
33
34   private String pdbFile;
35
36   private String chain;
37
38   private int pdbResNum;
39
40   private int atomIndex;
41
42   /**
43    * Parses a Chimera atomspec e.g. #1:12.A to construct an AtomSpec model (with
44    * null pdb file name)
45    * 
46    * <pre>
47    * Chimera format: 
48    *    #1.2:12-20.A     model 1, submodel 2, chain A, atoms 12-20
49    * ChimeraX format:
50    *    #1.2/A:12-20
51    * </pre>
52    * 
53    * @param spec
54    * @param chimeraX
55    * @return
56    * @throw IllegalArgumentException if the spec cannot be parsed, or represents
57    *        more than one residue
58    * @see https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec.html
59    * @see http://rbvi.ucsf.edu/chimerax/docs/user/commands/atomspec.html
60    */
61   public static AtomSpec fromChimeraAtomspec(String spec, boolean chimeraX)
62   {
63     int modelSeparatorPos = spec.indexOf(chimeraX ? "/" : ":");
64     if (modelSeparatorPos == -1)
65     {
66       throw new IllegalArgumentException(spec);
67     }
68
69     int hashPos = spec.indexOf("#");
70     if (hashPos == -1 && modelSeparatorPos != 0)
71     {
72       // # is missing but something precedes : - reject
73       throw new IllegalArgumentException(spec);
74     }
75
76     String modelSubmodel = spec.substring(hashPos + 1, modelSeparatorPos);
77     int modelId = 0;
78     try
79     {
80       int subModelPos = modelSubmodel.indexOf(".");
81       modelId = Integer.valueOf(
82               subModelPos > 0 ? modelSubmodel.substring(0, subModelPos)
83                       : modelSubmodel);
84     } catch (NumberFormatException e)
85     {
86       // ignore, default to model 0
87     }
88
89     /*
90      * now process what follows the model, either
91      * Chimera:  atoms.chain
92      * ChimeraX: chain:atoms
93      */
94     String atomsAndChain = spec.substring(modelSeparatorPos + 1);
95     String[] tokens = atomsAndChain.split(chimeraX ? "\\:" : "\\.");
96     String atoms = tokens.length == 1 ? atomsAndChain
97             : (chimeraX ? tokens[1] : tokens[0]);
98     int resNum = 0;
99     try
100     {
101       resNum = Integer.parseInt(atoms);
102     } catch (NumberFormatException e)
103     {
104       // could be a range e.g. #1:4-7.B
105       throw new IllegalArgumentException(spec);
106     }
107
108     String chainId = tokens.length == 1 ? ""
109             : (chimeraX ? tokens[0] : tokens[1]);
110
111     return new AtomSpec(modelId, chainId, resNum, 0);
112   }
113
114   /**
115    * Constructor
116    * 
117    * @param pdbFile
118    * @param chain
119    * @param resNo
120    * @param atomNo
121    */
122   public AtomSpec(String pdbFile, String chain, int resNo, int atomNo)
123   {
124     this.pdbFile = pdbFile;
125     this.chain = chain;
126     this.pdbResNum = resNo;
127     this.atomIndex = atomNo;
128   }
129
130   /**
131    * Constructor
132    * 
133    * @param modelId
134    * @param chainId
135    * @param resNo
136    * @param atomNo
137    */
138   public AtomSpec(int modelId, String chainId, int resNo, int atomNo)
139   {
140     this.modelNo = modelId;
141     this.chain = chainId;
142     this.pdbResNum = resNo;
143     this.atomIndex = atomNo;
144   }
145
146   public String getPdbFile()
147   {
148     return pdbFile;
149   }
150
151   public String getChain()
152   {
153     return chain;
154   }
155
156   public int getPdbResNum()
157   {
158     return pdbResNum;
159   }
160
161   public int getAtomIndex()
162   {
163     return atomIndex;
164   }
165
166   public int getModelNumber()
167   {
168     return modelNo;
169   }
170
171   public void setPdbFile(String file)
172   {
173     pdbFile = file;
174   }
175
176   @Override
177   public String toString()
178   {
179     return "pdbFile: " + pdbFile + ", chain: " + chain + ", res: "
180             + pdbResNum + ", atom: " + atomIndex;
181   }
182 }