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 */
021package org.biojava.nbio.structure.io;
022
023import org.biojava.nbio.structure.jama.Matrix;
024import org.biojava.nbio.structure.quaternary.BioAssemblyInfo;
025import org.biojava.nbio.structure.quaternary.BiologicalAssemblyTransformation;
026//import org.slf4j.Logger;
027//import org.slf4j.LoggerFactory;
028
029import java.util.*;
030
031/**
032 * Parses REMARK 350 records in a PDB file and creates transformations to
033 * construct the quaternary structure of a protein from an asymmetric unit
034 *
035 * @author Peter Rose
036 * @author Andreas Prlic
037 *
038 */
039public class PDBBioAssemblyParser {
040
041        //private static final Logger logger = LoggerFactory.getLogger(PDBBioAssemblyParser.class);
042
043        private Integer currentBioMolecule = null;
044        private List<String> currentChainIDs = new ArrayList<String>();
045        private Matrix currentMatrix = null;
046        private double[] shift = null;
047        private Map<Integer,BioAssemblyInfo> transformationMap = new HashMap<Integer, BioAssemblyInfo>();
048        private int modelNumber = 1;
049
050        private List<BiologicalAssemblyTransformation> transformations;
051
052        /**
053         * Parses REMARK 350 line. See format description:
054         * http://www.wwpdb.org/documentation/format33/remarks2.html
055         *
056         * @param line
057         */
058        public void pdb_REMARK_350_Handler(String line) {
059
060                if (line.startsWith("REMARK 350 BIOMOLECULE:")) {
061                    initialize();
062                        currentBioMolecule = Integer.parseInt(line.substring(24).trim());
063
064                } 
065                // not parsing anymore the size (from biojava 5.0), thus this is not needed anymore
066                // eventually if needed this could be used to
067                // infer if bioassembly is author or software determined
068                //else if ( line.matches("REMARK 350 \\w+ DETERMINED BIOLOGICAL UNIT:.*" ) ||
069                //                      line.matches("REMARK 350 \\w+ DETERMINED QUATERNARY STRUCTURE:.*" )) {
070                        // text can be :
071                        // author determined biological unit
072                        // software determined quaternary structure
073                //} 
074                else if ( line.startsWith("REMARK 350 APPLY THE FOLLOWING TO CHAINS:")) {
075                        currentChainIDs.clear();
076                        addToCurrentChainList(line);
077
078                } else if ( line.startsWith("REMARK 350 IN ADDITION APPLY THE FOLLOWING TO CHAINS:")) {
079                        currentChainIDs.clear();
080                        addToCurrentChainList(line);
081
082                } else if ( line.startsWith("REMARK 350") && line.contains("AND CHAINS:")) {
083                        addToCurrentChainList(line);
084
085                } else if ( line.startsWith("REMARK 350   BIOMT")) {
086                        if (readMatrix(line)) {
087                                saveMatrix();
088                                modelNumber++;
089                        }
090                }
091        }
092
093        /**
094         * Returns a map of bioassembly transformations
095         * @return
096         */
097        public Map<Integer, BioAssemblyInfo> getTransformationMap() {
098                return transformationMap;
099        }
100
101        /**
102         * Parses a row of a BIOMT matrix in a REMARK 350 record.
103         * Example: REMARK 350   BIOMT1   2  1.000000  0.000000  0.000000        0.00000
104         * @param line
105         * @return true if 3rd line of matrix has been parsed (matrix is complete)
106         */
107        private boolean readMatrix(String line) {
108                // split by one or more spaces
109                String[] items = line.split("[ ]+");
110
111                // parse BIOMTx, where x is the position in the matrix
112                String pos = items[2].substring(5);
113                int row = Integer.parseInt(pos);
114                if (row == 1) {
115                        currentMatrix = Matrix.identity(3,3);
116                        shift = new double[3];
117                }
118
119                currentMatrix.set((row-1), 0,Float.parseFloat(items[4]));
120                currentMatrix.set((row-1), 1,Float.parseFloat(items[5]));
121                currentMatrix.set((row-1), 2,Float.parseFloat(items[6]));
122                shift[row-1] = Float.parseFloat(items[7]);
123
124                // return true if 3rd row of matrix has been processed
125                return row == 3;
126        }
127
128        /**
129         * Saves transformation matrix for the list of current chains
130         */
131        private void saveMatrix() {
132
133                for (String chainId : currentChainIDs) {
134                        BiologicalAssemblyTransformation transformation = new BiologicalAssemblyTransformation();
135                        transformation.setRotationMatrix(currentMatrix.getArray());
136                        transformation.setTranslation(shift);
137                        transformation.setId(String.valueOf(modelNumber));
138                        transformation.setChainId(chainId);
139                        transformations.add(transformation);
140                }
141
142                if (!transformationMap.containsKey(currentBioMolecule)) {
143                        BioAssemblyInfo bioAssembly = new BioAssemblyInfo();
144                        bioAssembly.setId(currentBioMolecule);
145                        bioAssembly.setTransforms(transformations);
146                        transformationMap.put(currentBioMolecule,bioAssembly);
147                }
148        }
149
150        /**
151         * Parses list of chain ids (A, B, C, etc.)
152         */
153        private void addToCurrentChainList(String line) {
154                int index = line.indexOf(":");
155                String chainList = line.substring(index+1).trim();
156                // split by spaces or commas
157                String[] chainIds = chainList.split("[ ,]+");
158                currentChainIDs.addAll(Arrays.asList(chainIds));
159        }
160
161        private void initialize() {
162                transformations = new ArrayList<BiologicalAssemblyTransformation>();
163                currentMatrix = Matrix.identity(3,3);
164                currentBioMolecule = null;
165                shift = new double[3];
166                modelNumber = 1;
167        }
168        
169        /**
170         * Set the macromolecularSize fields of the parsed bioassemblies.
171         * This can only be called after the full PDB file has been read so that
172         * all the info for all bioassemblies has been gathered.
173         * Note that an explicit method to set the field is necessary here because 
174         * in PDB files the transformations contain only the author chain ids, corresponding 
175         * to polymeric chains, whilst in mmCIF files the transformations 
176         * contain all asym ids of both polymers and non-polymers.
177         */
178        public void setMacromolecularSizes() {
179                for (BioAssemblyInfo bioAssembly : transformationMap.values()) {
180                        bioAssembly.setMacromolecularSize(bioAssembly.getTransforms().size());
181                }
182        }
183}