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 javax.vecmath.Matrix4d;
028
029/**
030 * A general implementation of a BlockSet to store a flexible part of a multiple
031 * alignment.
032 *
033 * @author Aleix Lafita
034 * @since 4.1.0
035 *
036 */
037public class BlockSetImpl extends AbstractScoresCache implements Serializable,
038                BlockSet, Cloneable {
039
040        private static final long serialVersionUID = -1015791986000076089L;
041
042        // Member variables
043        private MultipleAlignment parent;
044        private List<Block> blocks;
045
046        // Cache variables (can be updated)
047        private List<Matrix4d> pose; // Transformation matrices
048        private int length;
049        private int coreLength;
050        private List<Integer> alignResCounts;
051
052        /**
053         * Constructor. Links also the parent to this instance by adding the
054         * BlockSet to the parent's List.
055         *
056         * @param alignment
057         *            MultipleAlignment parent of the BlockSet.
058         * @return BlockSet an instance linked to the parent alignment.
059         */
060        public BlockSetImpl(MultipleAlignment alignment) {
061
062                parent = alignment;
063                if (parent != null)
064                        parent.getBlockSets().add(this);
065                blocks = null;
066
067                pose = null;
068                length = -1; // Value -1 reserved to indicate that has to be calculated
069                coreLength = -1;
070                alignResCounts = null; // Value null means not yet calculated
071        }
072
073        /**
074         * Copy constructor. Makes also a deep copy of all constituent {@link Block}
075         * s.
076         *
077         * @param bs
078         *            BlockSet object to be copied.
079         * @return BlockSet an identical copy of the input object.
080         */
081        public BlockSetImpl(BlockSetImpl bs) {
082
083                super(bs); // This copies the cached scores
084                this.parent = bs.parent;
085                this.length = bs.length;
086                this.coreLength = bs.coreLength;
087
088                this.pose = null;
089                if (bs.pose != null) {
090                        // Make a deep copy of everything
091                        this.pose = new ArrayList<Matrix4d>();
092                        for (Matrix4d trans : bs.pose) {
093                                Matrix4d newTrans = (Matrix4d) trans.clone();
094                                pose.add(newTrans);
095                        }
096                }
097
098                blocks = null;
099                if (bs.blocks != null) {
100                        // Make a deep copy of everything
101                        this.blocks = new ArrayList<Block>();
102                        for (Block b : bs.blocks) {
103                                Block newB = b.clone();
104                                newB.setBlockSet(this);
105                                this.blocks.add(newB);
106                        }
107                }
108        }
109
110        @Override
111        public void clear() {
112                super.clear();
113                length = -1;
114                coreLength = -1;
115                alignResCounts = null;
116                pose = null;
117                for (Block a : getBlocks()) {
118                        a.clear();
119                }
120        }
121
122        @Override
123        public BlockSetImpl clone() {
124                return new BlockSetImpl(this);
125        }
126
127        @Override
128        public String toString() {
129                return "BlockSetImpl [blocks=" + blocks + ", pose=" + pose
130                                + ", length=" + length + ", coreLength=" + coreLength + "]";
131        }
132
133        @Override
134        public MultipleAlignment getMultipleAlignment() {
135                return parent;
136        }
137
138        @Override
139        public void setMultipleAlignment(MultipleAlignment parent) {
140                this.parent = parent;
141        }
142
143        @Override
144        public List<Block> getBlocks() {
145                if (blocks == null)
146                        blocks = new ArrayList<Block>();
147                return blocks;
148        }
149
150        @Override
151        public void setBlocks(List<Block> blocks) {
152                this.blocks = blocks;
153                for (Block b : blocks) {
154                        b.setBlockSet(this);
155                }
156        }
157
158        @Override
159        public List<Matrix4d> getTransformations() {
160                return pose;
161        }
162
163        @Override
164        public void setTransformations(List<Matrix4d> transformations) {
165                if (size() != transformations.size()) {
166                        throw new IllegalArgumentException(
167                                        "Wrong number of structures for this alignment");
168                }
169                pose = transformations;
170        }
171
172        @Override
173        public int length() {
174                if (length == -1)
175                        updateLength();
176                return length;
177        }
178
179        @Override
180        public int size() {
181                // Get the size from the variables that can contain the information
182                if (parent != null)
183                        return parent.size();
184                else if (getBlocks().size() == 0) {
185                        throw new IndexOutOfBoundsException(
186                                        "Empty BlockSet: number of Blocks == 0.");
187                } else
188                        return blocks.get(0).size();
189        }
190
191        @Override
192        public int getCoreLength() {
193                if (coreLength == -1)
194                        updateCoreLength();
195                return coreLength;
196        }
197
198        protected void updateLength() {
199                if (getBlocks().size() == 0) {
200                        throw new IndexOutOfBoundsException(
201                                        "Empty BlockSet: number of Blocks == 0.");
202                }
203                // Try to calculate it from the Block information
204                else {
205                        length = 0;
206                        for (Block block : blocks)
207                                length += block.length();
208                }
209        }
210
211        protected void updateCoreLength() {
212                if (getBlocks().size() == 0) {
213                        throw new IndexOutOfBoundsException(
214                                        "Empty BlockSet: number of Blocks == 0.");
215                }
216                // Try to calculate it from the Block information
217                else {
218                        coreLength = 0;
219                        for (Block block : blocks)
220                                coreLength += block.getCoreLength();
221                }
222        }
223
224        protected void updateCache() {
225                updateCoreLength();
226                updateLength();
227        }
228
229        @Override
230        public List<Integer> getAlignResCounts() {
231
232                if (alignResCounts != null)
233                        return alignResCounts;
234
235                alignResCounts = new ArrayList<Integer>(size());
236                for (int s = 0; s < size(); s++)
237                        alignResCounts.add(0);
238
239                for (Block b : blocks) {
240                        List<Integer> bcounts = b.getAlignResCounts();
241                        for (int s = 0; s < size(); s++)
242                                alignResCounts.set(s, alignResCounts.get(s) + bcounts.get(s));
243                }
244                return alignResCounts;
245        }
246
247}