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}