001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 * Created on Jun 17, 2010
021 * Author: ap3
022 *
023 */
024
025package org.biojava.nbio.structure;
026
027import java.io.Serializable;
028import java.io.StringWriter;
029
030/**
031 * Everything that is needed to uniquely describe a residue position
032 *
033 * @author Andreas Prlic
034 *
035 */
036public class ResidueNumber implements Serializable, Comparable<ResidueNumber>
037{
038
039        private static final long serialVersionUID = 1773011704758536083L;
040        private String chainName;
041        private Character insCode;
042        private Integer seqNum;
043
044        public ResidueNumber() {
045        }
046
047        public ResidueNumber(ResidueNumber o) {
048                this.chainName = o.chainName;
049                this.insCode = o.insCode;
050                this.seqNum = o.seqNum;
051        }
052
053        public ResidueNumber(String chainName, Integer residueNumber, Character insCode) {
054                this.chainName = chainName;
055                this.seqNum = residueNumber;
056                this.insCode = insCode;
057        }
058
059        public String getChainName()
060        {
061                return chainName;
062        }
063        public void setChainName(String chainName)
064        {
065                this.chainName = chainName;
066        }
067        public Character getInsCode()
068        {
069                return insCode;
070        }
071        public void setInsCode(Character insCode)
072        {
073                this.insCode = insCode;
074        }
075        public Integer getSeqNum()
076        {
077                return seqNum;
078        }
079        public void setSeqNum(Integer seqNum)
080        {
081                this.seqNum = seqNum;
082        }
083
084
085
086
087
088        @Override
089        public boolean equals(Object obj) {
090                if (this == obj)
091                        return true;
092                if (obj == null)
093                        return false;
094                if (getClass() != obj.getClass())
095                        return false;
096                ResidueNumber other = (ResidueNumber) obj;
097                if (chainName == null) {
098                        if (other.chainName != null)
099                                return false;
100                } else if (!chainName.equals(other.chainName))
101                        return false;
102                if (insCode == null) {
103                        if (other.insCode != null)
104                                return false;
105                } else if (!insCode.equals(other.insCode))
106                        return false;
107                if (seqNum == null) {
108                        if (other.seqNum != null)
109                                return false;
110                } else if (!seqNum.equals(other.seqNum))
111                        return false;
112
113                return true;
114        }
115
116        /**
117         * Check if the seqNum and insertion code are equivalent,
118         * ignoring the chain
119         * @param obj
120         * @return
121         */
122        public boolean equalsPositional(Object obj) {
123                if (this == obj)
124                        return true;
125                if (obj == null)
126                        return false;
127                if (getClass() != obj.getClass())
128                        return false;
129                ResidueNumber other = (ResidueNumber) obj;
130                if (insCode == null) {
131                        if (other.insCode != null)
132                                return false;
133                } else if (!insCode.equals(other.insCode))
134                        return false;
135                if (seqNum == null) {
136                        if (other.seqNum != null)
137                                return false;
138                } else if (!seqNum.equals(other.seqNum))
139                        return false;
140
141                return true;
142
143        }
144
145        @Override
146        public int hashCode() {
147                final int prime = 31;
148                int result = 1;
149                result = prime * result + ((chainName == null) ? 0 : chainName.hashCode());
150                result = prime * result + ((insCode == null) ? 0 : insCode.hashCode());
151                result = prime * result + ((seqNum == null) ? 0 : seqNum.hashCode());
152                return result;
153        }
154
155        /**
156         * @return The residue number and insertion code as a string, eg "74A"
157         * @see java.lang.Object#toString()
158         */
159        @Override
160        public String toString() {
161
162                StringWriter writer = new StringWriter();
163                //         if ( chainName != null){
164                //                 writer.append(chainName);
165                //                 writer.append(":");
166                //         }
167                writer.append(String.valueOf(seqNum));
168                if (  insCode != null && ( insCode != ' '))
169                        writer.append(insCode);
170
171                return writer.toString();
172        }
173
174        /**
175         * @return The chain, number, and insertion code as a string, eg "B  74A" or "A    1 "
176         */
177        public String toPDB() {
178                String insCodeS ;
179                if ( insCode != null)
180                        insCodeS = insCode+"";
181                else insCodeS = " ";
182                return String.format("%s%4d%-2s", chainName, seqNum, insCodeS);
183        }
184
185
186        /** Convert a string representation of a residue number to a residue number object.
187         * The string representation can be a integer followed by a character.
188         *
189         * @param pdb_code
190         * @return a ResidueNumber object, or null if the input was null
191         */
192        public static ResidueNumber fromString(String pdb_code) {
193                if(pdb_code == null)
194                        return null;
195
196                ResidueNumber residueNumber = new ResidueNumber();
197                Integer resNum = null;
198                String icode = null;
199
200                try {
201                        resNum = Integer.parseInt(pdb_code);
202                } catch ( NumberFormatException e){
203                        // there is an insertion code..
204
205                        // Split at any position that's either:
206                        // preceded by a digit and followed by a non-digit, or
207                        // preceded by a non-digit and followed by a digit.
208                        String[] spl = pdb_code.split("(?<=\\d)(?=\\D)|(?<=\\D)(?=\\d)");
209                        if ( spl.length == 2){
210                                resNum = Integer.parseInt(spl[0]);
211                                icode = spl[1];
212                        }
213
214                }
215
216                residueNumber.setSeqNum(resNum);
217                if ( icode == null)
218                        residueNumber.setInsCode(null);
219                else if ( icode.length() > 0)
220                        residueNumber.setInsCode(icode.charAt(0));
221                return residueNumber;
222        }
223
224
225        /**
226         * Compare residue numbers by chain, sequence number, and insertion code
227         */
228        @Override
229        public int compareTo(ResidueNumber other) {
230
231                // chain id
232                if (chainName != null && other.chainName != null) {
233                        if (!chainName.equals(other.chainName)) return chainName.compareTo(other.chainName);
234                }
235                if (chainName != null && other.chainName == null) {
236                        return 1;
237                } else if (chainName == null && other.chainName != null) {
238                        return -1;
239                }
240
241                return compareToPositional(other);
242        }
243
244        /**
245         * Compare residue numbers by sequence number and insertion code,
246         * ignoring the chain
247         * @param other
248         * @return
249         */
250        public int compareToPositional(ResidueNumber other) {
251                // sequence number
252                if (seqNum != null && other.seqNum != null) {
253                        if (!seqNum.equals(other.seqNum)) return seqNum.compareTo(other.seqNum);
254                }
255                if (seqNum != null && other.seqNum == null) {
256                        return 1;
257                } else if (seqNum == null && other.seqNum != null) {
258                        return -1;
259                }
260
261                // insertion code
262                if (insCode != null && other.insCode != null) {
263                        if (!insCode.equals(other.insCode)) return insCode.compareTo(other.insCode);
264                }
265                if (insCode != null && other.insCode == null) {
266                        return 1;
267                } else if (insCode == null && other.insCode != null) {
268                        return -1;
269                }
270
271                return 0;
272        }
273
274        public String printFull() {
275                final String chain = chainName==null? "" : chainName;
276                return chain + "_" + toString();
277        }
278
279}