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