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.align.multiple;
022
023import java.io.Serializable;
024import java.util.ArrayList;
025import java.util.List;
026import java.util.Locale;
027
028import org.biojava.nbio.structure.Atom;
029import org.biojava.nbio.structure.StructureException;
030import org.biojava.nbio.structure.StructureIdentifier;
031
032/**
033 * A general implementation of a {@link MultipleAlignment}.
034 *
035 * @author Aleix Lafita
036 * @since 4.1.0
037 *
038 */
039public class MultipleAlignmentImpl extends AbstractScoresCache implements
040                Serializable, MultipleAlignment, Cloneable {
041
042        private static final long serialVersionUID = 3432043794125805139L;
043
044        private MultipleAlignmentEnsemble parent;
045        private List<BlockSet> blockSets;
046
047        // Cache variables (can be updated)
048        private int length;
049        private int coreLength;
050        private List<Integer> alignResCounts;
051        private List<Double> coverages;
052
053        /**
054         * Default Constructor. Empty alignment. No structures assigned.
055         *
056         */
057        public MultipleAlignmentImpl() {
058                this(new MultipleAlignmentEnsembleImpl()); // assign an empty ensemble.
059        }
060
061        /**
062         * Constructor linking to an existing ensemble. Automatically adds this
063         * alignment to the parent ensemble.
064         *
065         * @param ensemble
066         *            parent MultipleAlignmentEnsemble.
067         */
068        public MultipleAlignmentImpl(MultipleAlignmentEnsemble ensemble) {
069
070                super();
071                parent = ensemble;
072                if (parent != null)
073                        parent.getMultipleAlignments().add(this);
074
075                blockSets = null;
076
077                length = -1; // Value -1 reserved to indicate that has to be calculated
078                coreLength = -1;
079                alignResCounts = null; // Value null means not set
080                coverages = null;
081        }
082
083        /**
084         * Copy constructor. Recursively copies member BlockSets.
085         *
086         * @param ma
087         *            MultipleAlignmentImpl to copy.
088         */
089        public MultipleAlignmentImpl(MultipleAlignmentImpl ma) {
090
091                super(ma); // Copy the scores
092                parent = ma.parent;
093
094                length = ma.length;
095                coreLength = ma.coreLength;
096
097                blockSets = null;
098                if (ma.blockSets != null) {
099                        // Make a deep copy of everything
100                        this.blockSets = new ArrayList<>();
101                        for (BlockSet bs : ma.blockSets) {
102                                BlockSet newBS = bs.clone();
103                                newBS.setMultipleAlignment(this);
104                                this.blockSets.add(newBS);
105                        }
106                }
107        }
108
109        @Override
110        public void clear() {
111                super.clear();
112                length = -1;
113                coreLength = -1;
114                alignResCounts = null;
115                for (BlockSet a : getBlockSets()) {
116                        a.clear();
117                }
118        }
119
120        @Override
121        public MultipleAlignmentImpl clone() {
122                return new MultipleAlignmentImpl(this);
123        }
124
125        @Override
126        public String toString() {
127                List<String> ids = new ArrayList<>(parent
128                                .getStructureIdentifiers().size());
129                for (StructureIdentifier i : parent.getStructureIdentifiers()) {
130                        ids.add(i.getIdentifier());
131                }
132                String resume = "Structures:" + ids + " \nAlgorithm:"
133                                + parent.getAlgorithmName() + "_" + parent.getVersion()
134                                + " \nBlockSets: " + getBlockSets().size() + " \nBlocks: "
135                                + getBlocks().size() + " \nLength: " + length()
136                                + " \nCore Length: " + getCoreLength();
137                for (String score : getScores()) {
138                        resume += " \n" + score + ": ";
139                        resume += String.format(Locale.US, "%.2f", getScore(score));
140                }
141                return resume;
142        }
143
144        @Override
145        public List<BlockSet> getBlockSets() {
146                if (blockSets == null)
147                        blockSets = new ArrayList<>();
148                return blockSets;
149        }
150
151        @Override
152        public List<Block> getBlocks() {
153                List<Block> blocks = new ArrayList<>();
154                for (BlockSet bs : getBlockSets()) {
155                        blocks.addAll(bs.getBlocks());
156                }
157                return blocks;
158        }
159
160        @Override
161        public void setBlockSets(List<BlockSet> blockSets) {
162                this.blockSets = blockSets;
163        }
164
165        @Override
166        public BlockSet getBlockSet(int index) {
167                return blockSets.get(index);
168        }
169
170        @Override
171        public Block getBlock(int index) {
172                List<Block> blocks = getBlocks();
173                return blocks.get(index);
174        }
175
176        @Override
177        public List<Atom[]> getAtomArrays() {
178                return parent.getAtomArrays();
179        }
180
181        @Override
182        public StructureIdentifier getStructureIdentifier(int index) {
183                return parent.getStructureIdentifiers().get(index);
184        }
185
186        @Override
187        public int size() {
188                return parent.size();
189        }
190
191        @Override
192        public int length() {
193                if (length < 0)
194                        updateLength();
195                return length;
196        }
197
198        @Override
199        public int getCoreLength() {
200                if (coreLength < 0)
201                        updateCoreLength();
202                return coreLength;
203        }
204
205        /**
206         * Force recalculation of the length (aligned columns) based on the BlockSet
207         * lengths.
208         */
209        protected void updateLength() {
210                if (getBlockSets().size() == 0) {
211                        throw new IndexOutOfBoundsException(
212                                        "Empty MultipleAlignment: blockSets size == 0.");
213                } // Otherwise try to calculate it from the BlockSet information
214                else {
215                        length = 0;
216                        for (BlockSet blockSet : blockSets)
217                                length += blockSet.length();
218                }
219        }
220
221        /**
222         * Force recalculation of the core length (ungapped columns) based on the
223         * BlockSet core lengths.
224         */
225        protected void updateCoreLength() {
226                if (getBlockSets().size() == 0) {
227                        throw new IndexOutOfBoundsException(
228                                        "Empty MultipleAlignment: blockSets size == 0.");
229                } // Otherwise try to calculate it from the BlockSet information
230                else {
231                        coreLength = 0;
232                        for (BlockSet blockSet : blockSets)
233                                coreLength += blockSet.getCoreLength();
234                }
235        }
236
237        /**
238         * Updates all cached properties
239         *
240         */
241        protected void updateCache() {
242                updateCoreLength();
243                updateLength();
244        }
245
246        @Override
247        public MultipleAlignmentEnsemble getEnsemble() {
248                return parent;
249        }
250
251        @Override
252        public void setEnsemble(MultipleAlignmentEnsemble parent) {
253                this.parent = parent;
254        }
255
256        @Override
257        public List<Integer> getAlignResCounts() {
258
259                if (alignResCounts != null)
260                        return alignResCounts;
261
262                alignResCounts = new ArrayList<>(size());
263                for (int s = 0; s < size(); s++)
264                        alignResCounts.add(0);
265
266                for (BlockSet bs : blockSets) {
267                        List<Integer> bscounts = bs.getAlignResCounts();
268                        for (int s = 0; s < size(); s++)
269                                alignResCounts.set(s, alignResCounts.get(s) + bscounts.get(s));
270                }
271                return alignResCounts;
272        }
273
274        @Override
275        public List<Double> getCoverages() {
276
277                if (coverages != null)
278                        return coverages;
279
280                List<Integer> counts = getAlignResCounts();
281                coverages = new ArrayList<>(size());
282                for (int s = 0; s < size(); s++)
283                        coverages.add(counts.get(s)
284                                        / (double) getAtomArrays().get(s).length);
285                return coverages;
286        }
287
288}