/*
* $Id: Alignment.java,v 1.11 2006/08/19 00:42:35 ahmed Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package jaligner;
import jaligner.matrix.Matrix;
import jaligner.util.Commons;
import java.text.DecimalFormat;
/**
* Holds the output of a pairwise sequences alignment.
*
* @author Ahmed Moustafa (ahmed@users.sf.net)
*/
public final class Alignment {
/**
* Gap character
*/
public static final char GAP = '-';
/**
* Default name for sequence #1
*/
private static final String SEQUENCE1 = "jaligner_1";
/**
* Default name for sequence #2
*/
private static final String SEQUENCE2 = "jaligner_2";
/**
* Scoring matrix
*/
private Matrix matrix;
/**
* Gap open cost
*/
private float open;
/**
* Gap extend cost
*/
private float extend;
/**
* Alignment score
*/
private float score;
/**
* Aligned sequence #1
*/
private char[] sequence1;
/**
* Name of sequence #1
*/
private String name1;
/**
* Alignment start location in sequence #1
*/
private int start1;
/**
* Aligned sequence #2
*/
private char[] sequence2;
/**
* Name of sequence #2
*/
private String name2;
/**
* Alignment start location in sequence #2
*/
private int start2;
/**
* Markup line
*/
private char[] markupLine;
/**
* Count of identical locations
*/
private int identity;
/**
* Count of similar locations
*/
private int similarity;
/**
* Count of gap locations
*/
private int gaps;
private Sequence originalSequence1;
private Sequence originalSequence2;
private int ungappedStart, ungappedEnd;
/**
* Constructor for Alignment
*/
public Alignment() {
super();
}
/**
* @return Returns the extend.
*/
public float getExtend() {
return extend;
}
/**
* @param extend
* The extend to set.
*/
public void setExtend(float extend) {
this.extend = extend;
}
/**
* @return Returns the matrix.
*/
public Matrix getMatrix() {
return matrix;
}
/**
* @param matrix
* The matrix to set.
*/
public void setMatrix(Matrix matrix) {
this.matrix = matrix;
}
/**
* @return Returns the name1.
*/
public String getName1() {
return name1 == null || name1.trim().length() == 0 ? SEQUENCE1 : name1;
}
/**
* @param name1
* The name1 to set.
*/
public void setName1(String name1) {
this.name1 = name1;
}
/**
* @return Returns the name2.
*/
public String getName2() {
return name2 == null || name2.trim().length() == 0 ? SEQUENCE2 : name2;
}
/**
* @param name2
* The name2 to set.
*/
public void setName2(String name2) {
this.name2 = name2;
}
/**
* @return Returns the open.
*/
public float getOpen() {
return open;
}
/**
* @param open
* The open to set.
*/
public void setOpen(float open) {
this.open = open;
}
/**
* @return Returns the score.
*/
public float getScore() {
return score;
}
/**
* @param score
* The score to set.
*/
public void setScore(float score) {
this.score = score;
}
/**
* Returns the length of the alignment
*
* @return Alignment length
*/
public int getLength() {
return this.sequence1.length;
}
/**
* @return Returns the sequence1.
*/
public char[] getSequence1() {
return sequence1;
}
/**
* @param sequence1
* The sequence1 to set.
*/
public void setSequence1(char[] sequence1) {
this.sequence1 = sequence1;
}
/**
* @return Returns the sequence2.
*/
public char[] getSequence2() {
return sequence2;
}
/**
* @param sequence2
* The sequence2 to set.
*/
public void setSequence2(char[] sequence2) {
this.sequence2 = sequence2;
}
/**
* @return Returns the start1.
*/
public int getStart1() {
return start1;
}
public int getUngappedStart(){
return ungappedStart;
}
public int getUngappedEnd(){
return ungappedEnd;
}
/**
* @param start1
* The start1 to set.
*/
public void setStart1(int start1) {
this.start1 = start1;
}
/**
* @return Returns the start2.
*/
public int getStart2() {
return start2;
}
/**
* @param start2
* The start2 to set.
*/
public void setStart2(int start2) {
this.start2 = start2;
}
/**
* @return Returns the gaps.
*/
public int getGaps() {
return gaps;
}
/**
* @param gaps
* The gaps to set.
*/
public void setGaps(int gaps) {
this.gaps = gaps;
}
/**
* @return Returns the identity.
*/
public int getIdentity() {
return identity;
}
/**
* @param identity
* The identity to set.
*/
public void setIdentity(int identity) {
this.identity = identity;
}
/**
* @return Returns the markupLine.
*/
public char[] getMarkupLine() {
return markupLine;
}
/**
* @param markupLine
* The markupLine to set.
*/
public void setMarkupLine(char[] markupLine) {
this.markupLine = markupLine;
}
/**
* @return Returns the similarity.
*/
public int getSimilarity() {
return similarity;
}
/**
* @param similarity
* The similarity to set.
*/
public void setSimilarity(int similarity) {
this.similarity = similarity;
}
/**
* Returns a summary for alignment
*
* @return {@link String} alignment summary
*/
public String getSummary() {
StringBuffer buffer = new StringBuffer();
DecimalFormat f1 = new DecimalFormat("0.00");
DecimalFormat f2 = new DecimalFormat("0.00%");
int length = getSequence1().length;
buffer.append("Sequence #1: " + getName1());
buffer.append(Commons.getLineSeparator());
buffer.append("Sequence #2: " + getName2());
buffer.append(Commons.getLineSeparator());
buffer.append("Length #1: " + getOriginalSequence1().length());
buffer.append(Commons.getLineSeparator());
buffer.append("Length #2: " + getOriginalSequence2().length());
buffer.append(Commons.getLineSeparator());
buffer.append("Matrix: "
+ (matrix.getId() == null ? "" : matrix.getId()));
buffer.append(Commons.getLineSeparator());
buffer.append("Gap open: " + open);
buffer.append(Commons.getLineSeparator());
buffer.append("Gap extend: " + extend);
buffer.append(Commons.getLineSeparator());
buffer.append("Length: " + length);
buffer.append(Commons.getLineSeparator());
buffer.append("Identity: " + identity + "/" + length + " ("
+ f2.format(identity / (float) length) + ")");
buffer.append(Commons.getLineSeparator());
buffer.append("Similarity: " + similarity + "/" + length + " ("
+ f2.format(similarity / (float) length) + ")");
buffer.append(Commons.getLineSeparator());
buffer.append("Gaps: " + gaps + "/" + length + " ("
+ f2.format(gaps / (float) length) + ")");
buffer.append(Commons.getLineSeparator());
buffer.append("Score: " + f1.format(score));
buffer.append(Commons.getLineSeparator());
return buffer.toString();
}
/**
* Calculate the score of the alignment, not using the score field (the
* function only uses sequence1, sequence2, matrix and gap penalties).
*
* @return the calculated score (By: Bram Minnaert)
*/
public float calculateScore() {
// The calculated score
float calcScore = 0;
// In the previous step there was a gap in the first sequence
boolean previous1wasGap = false;
// In the previous step there was a gap in the second sequence
boolean previous2wasGap = false;
int start = 0;
int end = sequence1.length - 1;
char c1, c2; // the next character
for (int i = start; i <= end; i++) {
c1 = sequence1[i];
c2 = sequence2[i];
// the next character in the first sequence is a gap
if (c1 == GAP) {
if (previous1wasGap) {
calcScore -= extend;
} else {
calcScore -= open;
}
previous1wasGap = true;
previous2wasGap = false;
}
// the next character in the second sequence is a gap
else if (c2 == GAP) {
if (previous2wasGap) {
calcScore -= extend;
} else {
calcScore -= open;
}
previous1wasGap = false;
previous2wasGap = true;
}
// the next characters in boths sequences are not gaps
else {
calcScore += matrix.getScore(c1, c2);
previous1wasGap = false;
previous2wasGap = false;
}
}
return calcScore;
}
/**
* Calculate the score of the alignment without the terminal gaps.
*
*/
public float getScoreWithNoTerminalGaps() {
// The calculated score
float calcScore = 0;
// In the previous step there was a gap in the first sequence
boolean previous1wasGap = false;
// In the previous step there was a gap in the second sequence
boolean previous2wasGap = false;
int start = 0;
int end = sequence1.length - 1;
if (sequence1[start] == GAP) {
while (sequence1[start] == GAP) {
start++;
}
} else if (sequence2[start] == GAP) {
while (sequence2[start] == GAP) {
start++;
}
}
if (sequence1[end] == GAP) {
while (sequence1[end] == GAP) {
end--;
}
} else if (sequence2[end] == GAP) {
while (sequence2[end] == GAP) {
end--;
}
}
ungappedStart=start;
ungappedEnd=end;
char c1, c2; // the next character
for (int i = start; i <= end; i++) {
c1 = sequence1[i];
c2 = sequence2[i];
// the next character in the first sequence is a gap
if (c1 == GAP) {
if (previous1wasGap) {
calcScore -= extend;
} else {
calcScore -= open;
}
previous1wasGap = true;
previous2wasGap = false;
}
// the next character in the second sequence is a gap
else if (c2 == GAP) {
if (previous2wasGap) {
calcScore -= extend;
} else {
calcScore -= open;
}
previous1wasGap = false;
previous2wasGap = true;
}
// the next characters in boths sequences are not gaps
else {
calcScore += matrix.getScore(c1, c2);
previous1wasGap = false;
previous2wasGap = false;
}
}
return calcScore;
}
public void setUngappedLimits(){
ungappedStart=0;
ungappedEnd=0;
}
/**
* Check if the calculated score matches the field score.
*
* @return true if equal, else false. (By: Bram Minnaert)
*/
public boolean checkScore() {
if (calculateScore() == score) {
return true;
} else {
return false;
}
}
/**
* Returns original {@link Sequence} #1
*
* @return original {@link Sequence} #1
*/
public Sequence getOriginalSequence1() {
return originalSequence1;
}
/**
*
* @param originalSequence1
*/
public void setOriginalSequence1(Sequence originalSequence1) {
this.originalSequence1 = originalSequence1;
}
/**
* Returns original {@link Sequence} #2
*
* @return original {@link Sequence} #2
*/
public Sequence getOriginalSequence2() {
return originalSequence2;
}
/**
*
* @param originalSequence2
*/
public void setOriginalSequence2(Sequence originalSequence2) {
this.originalSequence2 = originalSequence2;
}
/**
* Returns the number of gaps of the aligned sequence #1
*
* @return the number of gaps of the aligned sequence #1
*/
public int getGaps1() {
int count = 0;
for (int i = 0, n = sequence1.length; i < n; i++) {
if (sequence1[i] == Alignment.GAP) {
count++;
}
}
return count;
}
/**
* Returns the number of gaps of the aligned sequence #2
*
* @return the number of gaps of the aligned sequence #2
*/
public int getGaps2() {
int count = 0;
for (int i = 0, n = sequence2.length; i < n; i++) {
if (sequence2[i] == Alignment.GAP) {
count++;
}
}
return count;
}
public String getStuffy() {
StringBuffer sb = new StringBuffer();
for (int i = 0, n = sequence2.length; i < n; i++) {
sb.append(sequence2[i]);
}
return sb.toString();
}
/**
* Adds this alignment to another alignment, the order is important.
*
* @param a1
* The 1st alignment
* @param a2
* The 2nd alignment
* @return the sum of two alignment
*/
public static Alignment add(Alignment a1, Alignment a2) {
if (a1 == null) {
if (a2 == null) {
return null;
} else {
return copy(a2);
}
} else {
if (a2 == null) {
return copy(a1);
} else {
Alignment sum = new Alignment();
StringBuffer buffer = new StringBuffer();
buffer.append(a1.getOriginalSequence1());
buffer.append(a2.getOriginalSequence1());
sum.setOriginalSequence1(new Sequence(buffer.toString()));
buffer = new StringBuffer();
buffer.append(a1.getOriginalSequence2());
buffer.append(a2.getOriginalSequence2());
sum.setOriginalSequence2(new Sequence(buffer.toString()));
buffer = new StringBuffer();
buffer.append(a1.getSequence1());
buffer.append(a2.getSequence1());
sum.setSequence1(buffer.toString().toCharArray());
buffer = new StringBuffer();
buffer.append(a1.getSequence2());
buffer.append(a2.getSequence2());
sum.setSequence2(buffer.toString().toCharArray());
buffer = new StringBuffer();
buffer.append(a1.getMarkupLine());
buffer.append(a2.getMarkupLine());
sum.setMarkupLine(buffer.toString().toCharArray());
sum.setScore(a1.getScore() + a2.getScore());
sum.setGaps(a1.getGaps() + a2.getGaps());
sum.setStart1(a1.getStart1());
sum.setStart2(a1.getStart2());
sum.setExtend(a1.getExtend());
sum.setOpen(a1.getOpen());
sum.setMatrix(a1.getMatrix());
return sum;
}
}
}
/**
* Copies an alignment to another alignment.
*
* @param alignment
* Alignment
*/
public static Alignment copy(Alignment alignment) {
Alignment copy = new Alignment();
copy.setSequence1(alignment.getSequence1());
copy.setSequence2(alignment.getSequence2());
copy.setMarkupLine(alignment.getMarkupLine());
copy.setScore(alignment.getScore());
copy.setGaps(alignment.getGaps());
copy.setStart1(alignment.getStart1());
copy.setStart2(alignment.getStart2());
copy.setExtend(alignment.getExtend());
copy.setOpen(alignment.getOpen());
copy.setMatrix(alignment.getMatrix());
copy.setOriginalSequence1(alignment.getOriginalSequence1());
copy.setOriginalSequence2(alignment.getOriginalSequence2());
return copy;
}
}