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