package aliview.sequencelist;
import java.awt.Point;
import java.awt.Rectangle;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.EventListener;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import javax.swing.AbstractListModel;
import javax.swing.DefaultListModel;
import javax.swing.JOptionPane;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.event.EventListenerList;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.ObjectUtils.Null;
import org.apache.log4j.Logger;
import utils.DialogUtils;
import utils.nexus.CharSet;
import utils.nexus.CodonPos;
import utils.nexus.CodonPositions;
import aliview.AliView;
import aliview.AminoAcid;
import aliview.GeneticCode;
import aliview.NucleotideUtilities;
import aliview.alignment.AAHistogram;
import aliview.alignment.NotUsed_AATranslator;
import aliview.alignment.AliHistogram;
import aliview.alignment.Alignment;
import aliview.alignment.AlignmentMeta;
import aliview.alignment.NucleotideHistogram;
import aliview.importer.AlignmentImportException;
import aliview.importer.FileFormat;
import aliview.sequences.FileSequence;
import aliview.sequences.BasicSequence;
import aliview.sequences.InMemorySequence;
import aliview.sequences.Sequence;
import aliview.sequences.SequenceUtils;
public class AlignmentListModel implements ListModel, Iterable<Sequence>{
private static final String LF = System.getProperty("line.separator");
private static final long serialVersionUID = -8081215660929212156L;
private static final Logger logger = Logger.getLogger(AlignmentListModel.class);
List<Sequence> delegateSequences;
protected FileFormat fileFormat;
protected int sequenceType = SequenceUtils.TYPE_UNKNOWN;
private int selectionOffset;
private AlignmentSelectionModel selectionModel = new AlignmentSelectionModel(this);
protected EventListenerList listenerList = new EventListenerList();
// AliHistogram and other cached variables has to be volatile so no problems araise
// with the double lock synch strategy
// see: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
private volatile AliHistogram cachedHistogram;
private volatile int cachedLongestSequenceName;
private volatile int cachedLongestSequenceLength = -1;
private boolean isTranslated;
private Alignment alignment;
public AlignmentListModel() {
this.delegateSequences = new ArrayList<Sequence>();
}
public AlignmentListModel(List<Sequence> seqs) {
for(Sequence seq: seqs){
seq.setAlignmentModel(this);
}
this.delegateSequences = seqs;
fireSequenceIntervalAdded(0, seqs.size() - 1);
}
public AlignmentListModel(List<Sequence> seqs, FileFormat foundFormat) {
this.fileFormat = foundFormat;
for(Sequence seq: seqs){
seq.setAlignmentModel(this);
}
this.delegateSequences = seqs;
fireSequenceIntervalAdded(0, seqs.size() - 1);
}
public AlignmentListModel(AlignmentListModel template){
ArrayList<Sequence> seqClone = new ArrayList<Sequence>();
for (Sequence seq: template.delegateSequences) {
seqClone.add(seq.getCopy());
}
this.fileFormat = template.fileFormat;
this.delegateSequences = seqClone;
this.sequenceType = template.sequenceType;
fireSequencesChangedAllNew();
}
// ***************************************
// ListModel interface
// ***************************************
public int getSize() {
return delegateSequences.size();
}
public Sequence getElementAt(int index){
return delegateSequences.get(index);
}
public void addListDataListener(ListDataListener l) {
listenerList.add(ListDataListener.class, l);
}
public void removeListDataListener(ListDataListener l) {
listenerList.remove(ListDataListener.class, l);
}
// ***************************************
// End List model interface
// ***************************************
public void addAlignmentDataListener(AlignmentDataListener l) {
listenerList.add(AlignmentDataListener.class, l);
}
public void removeAlignmentDataListener(AlignmentDataListener l) {
listenerList.remove(AlignmentDataListener.class, l);
}
// ***************************************
// AbstractListModel
// ***************************************
/*
@Override
public void addAlignmentListDataListener(ListDataListener l) {
// TODO Auto-generated method stub
super.addListDataListener(l);
}
@Override
public void addListDataListener(ListDataListener l) {
// TODO Auto-generated method stub
super.addListDataListener(l);
}
@Override
public void removeListDataListener(ListDataListener l) {
// TODO Auto-generated method stub
super.removeListDataListener(l);
}
@Override
public ListDataListener[] getListDataListeners() {
// TODO Auto-generated method stub
return super.getListDataListeners();
}
@Override
protected void fireContentsChanged(Object source, int index0, int index1) {
// TODO Auto-generated method stub
super.fireContentsChanged(source, index0, index1);
}
@Override
protected void fireIntervalAdded(Object source, int index0, int index1) {
// TODO Auto-generated method stub
super.fireIntervalAdded(source, index0, index1);
}
@Override
protected void fireIntervalRemoved(Object source, int index0, int index1) {
// TODO Auto-generated method stub
super.fireIntervalRemoved(source, index0, index1);
}
@Override
public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
// TODO Auto-generated method stub
return super.getListeners(listenerType);
}
*/
// ***************************************
// End AbstractList model
// ***************************************
// ***************************************
// Iterableinterface
// ***************************************
public Iterator<Sequence> iterator() {
return delegateSequences.listIterator();
}
// ***************************************
// End Iterableinterface
// ***************************************
public void setSequences(List<Sequence> list){
if(list != null){
for(Sequence seq: list){
seq.setAlignmentModel(this);
}
this.delegateSequences = list;
fireSequencesChangedAllNew();
}
}
public List<Sequence> getDelegateSequences() {
return delegateSequences;
}
public AlignmentListModel getCopy(){
return new AlignmentListModel(this);
}
public AlignmentListModel getCopyShallow(){
AlignmentListModel copy = new AlignmentListModel();
copy.delegateSequences.addAll(this.delegateSequences);
copy.fileFormat = fileFormat;
copy.sequenceType = sequenceType;
return copy;
}
public Sequence get(int index) {
if(index >= delegateSequences.size()){
return null;
}
return delegateSequences.get(index);
}
/*
public void add(int index, Sequence seq) {
logger.info("add at=" + index);
delegateSequences.add(index, seq);
fireSequenceIntervalAdded(index, index);
}
public Sequence set(int index, Sequence element){
Sequence previous = sequences.set(index, element);
fireSequencesChanged(index, index);
return previous;
}
public void removeAt(int index) {
sequences.remove(index);
fireSequenceIntervalRemoved(index, index);
}
public void insertAt(Sequence seq, int index) {
sequences.add(index, seq);
fireSequenceIntervalAdded(index, index);
}
*/
// TODO these three set and add methods might give problems if there is no
// is adjusting method
public Sequence set(int index, Sequence sequence){
sequence.setAlignmentModel(this);
Sequence previous = delegateSequences.set(index, sequence);
// TODO Maybe add an adjusting parameter...
fireSequencesChanged(index, index);
return previous;
}
public void add(Sequence sequence) {
sequence.setAlignmentModel(this);
delegateSequences.add(sequence);
fireSequenceIntervalAdded(this.size() -1, this.size() - 1);
}
public void add(int index, Sequence seq) {
seq.setAlignmentModel(this);
delegateSequences.add(index, seq);
// TODO Maybe add an adjusting parameter...
fireSequenceIntervalAdded(index, index);
}
public void addAll(AlignmentListModel otherSeqModel, boolean setSelected) {
addAll(otherSeqModel.getDelegateSequencesCopy(), setSelected);
}
public void addAll(int index, AlignmentListModel otherSeqModel) {
for(Sequence seq: otherSeqModel.getDelegateSequencesCopy()){
seq.setAlignmentModel(this);
}
delegateSequences.addAll(index, otherSeqModel.getDelegateSequencesCopy());
fireSequenceIntervalAdded(index, index + otherSeqModel.getDelegateSequencesCopy().size());
}
public void addAll(List<Sequence> moreSeqs, boolean setSelected) {
for(Sequence seq: moreSeqs){
seq.setAlignmentModel(this);
}
logger.info("added all moreSeqs.size()" + moreSeqs.size());
delegateSequences.addAll(moreSeqs);
if(setSelected){
selectionModel.setSequenceSelection(moreSeqs);
}
//fireSequencesChangedAll();
fireSequenceIntervalAdded(this.size() - moreSeqs.size(), this.size() - 1);
}
public List<Sequence> getDelegateSequencesCopy(){
return new ArrayList<Sequence>(delegateSequences);
}
protected List<Sequence> getSequences() {
return delegateSequences;
}
public int getLongestSequenceLength(){
if(cachedLongestSequenceLength <= 0){
// this is double locked to avoid synchronized block after the cached initialization of variacle
// cached variable has to be declared volatile above
// see: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html and http://en.wikipedia.org/wiki/Double-checked_locking
synchronized(this){
if(cachedLongestSequenceLength <0){
int maxLen = 0;
for(int n = 0; n < delegateSequences.size(); n++){
int len = delegateSequences.get(n).getLength();
if(len > maxLen){
maxLen = len;
}
}
cachedLongestSequenceLength = maxLen;
}
}
}
return cachedLongestSequenceLength;
}
public int getShortestSequenceLength() {
int minLen = getLongestSequenceLength();
for(int n = 0; n < delegateSequences.size(); n++){
int len = delegateSequences.get(n).getLength();
if(len < minLen){
minLen = len;
}
}
return minLen;
}
public FileFormat getFileFormat() {
return this.fileFormat;
}
public void setFileFormat(FileFormat fileFormat) {
this.fileFormat = fileFormat;
}
public int getSequenceType() {
if(delegateSequences.size() > 0 && sequenceType == SequenceUtils.TYPE_UNKNOWN){
// TODO could figure out if not a sequence
int gapCount = 0;
int nucleotideCount = 0;
// Loop through 5000 bases or sequence length
Sequence testSeq = delegateSequences.get(0);
int maxLen = Math.min(5000, testSeq.getLength());
for(int n = 0; n < maxLen; n++){
byte base = testSeq.getBaseAtPos(n);
if(NucleotideUtilities.isGap(base)){
gapCount ++;
}else if(NucleotideUtilities.isNucleoticeOrIUPAC(base)){
nucleotideCount ++;
}
else{
// logger.info("other base=" + base);
}
}
// allow 1 wrong base
if(maxLen == 0 || (nucleotideCount + gapCount + 1 >= maxLen)){
this.sequenceType = SequenceUtils.TYPE_NUCLEIC_ACID;
}
else{
this.sequenceType = SequenceUtils.TYPE_AMINO_ACID;
}
}
return sequenceType;
}
public void reverseComplement(List<Sequence> seqs) {
for(Sequence seq : seqs){
seq.reverseComplement();
}
if(seqs.size() > 0){
fireSequencesChanged(seqs);
}
}
public void deleteSequence(Sequence seq) {
delegateSequences.remove(seq);
fireSequencesChangedAll();
}
public void deleteSequences(List<Sequence> toDelete) {
for(Sequence seq: toDelete){
delegateSequences.remove(seq);
}
fireSequencesChangedAll();
}
public List<Sequence> deleteFullySelectedSequences() {
List<Sequence> toDelete = getFullySelectedSequences();
deleteSequences(toDelete);
return toDelete;
}
public List<Sequence> getFullySelectedSequences(){
List<Sequence> fullySelected = new ArrayList<Sequence>();
for(Sequence seq: delegateSequences){
if(seq.isAllSelected()){
fullySelected.add(seq);
}
}
return fullySelected;
}
public ArrayList<Sequence> deleteEmptySequences(){
ArrayList<Sequence> toDelete = new ArrayList<Sequence>();
for(Sequence seq: delegateSequences){
if(seq.isEmpty()){
toDelete.add(seq);
}
}
deleteSequences(toDelete);
return toDelete;
}
public void moveSelectedSequencesToBottom() {
List<Sequence> selected = selectionModel.getSelectedSequences();
moveSequencesToBottom(selected);
}
public void moveSelectedSequencesToTop() {
List<Sequence> selected = selectionModel.getSelectedSequences();
moveSequencesToTop(selected);
}
public void moveSelectedSequencesUp() {
List<Sequence> selected = selectionModel.getSelectedSequences();
moveSequencesUp(selected);
}
public void moveSelectedSequencesDown() {
List<Sequence> selected = selectionModel.getSelectedSequences();
moveSequencesDown(selected);
}
public void moveSelectedSequencesTo(int index) {
List<Sequence> selected = selectionModel.getSelectedSequences();
moveSequencesTo(index, selected);
}
public void moveSequencesToBottom(List<Sequence> seqs) {
logger.info("removeAll");
delegateSequences.removeAll(seqs);
logger.info("addAll");
delegateSequences.addAll(seqs);
logger.info("seqChanged");
if(seqs.size() > 0){
fireSequencesChangedAll();
}
}
public void moveSequencesToTop(List<Sequence> seqs) {
delegateSequences.removeAll(seqs);
delegateSequences.addAll(0, seqs);
if(seqs.size() > 0){
fireSequencesChangedAll();
}
}
public void moveSequencesTo(int index, List<Sequence> seqs) {
if(index >= delegateSequences.size()){
index = delegateSequences.size() - 1;
}
if(index < 0){
index = 0;
}
// get current pos
int current = delegateSequences.indexOf(seqs.get(0));
int diff = current - index;
logger.info("diff" + diff);
// loop sequences up or down
if(diff > 0){
for(int n = 0; n < diff; n++){
moveSequencesUp(seqs);
}
}
if(diff < 0){
// Add seqs length because we are using the last index ad lead when counting
for(int n = 0; n <= Math.abs(diff) - seqs.size(); n++){
moveSequencesDown(seqs);
}
}
}
public void moveSequencesUp(List<Sequence> seqs){
logger.info("move seq up");
if(seqs == null || seqs.size() == 0){
return;
}
for(Sequence seq: seqs){
int index = delegateSequences.indexOf(seq);
// break if we are at top
if(index == 0){
break;
}
Sequence previous = (Sequence) delegateSequences.set(index - 1, seq);
delegateSequences.set(index,previous);
}
logger.info("seqs.size()" + seqs.size());
if(seqs.size() > 0){
fireSequencesChangedAll();
}
}
public void moveSequencesDown(List<Sequence> seqs) {
logger.info("move seq down");
if(seqs == null || seqs.size() == 0){
return;
}
// Has to be done reverse (otherwise index problem)
for(int n = seqs.size() - 1; n >=0 ; n--){
Sequence seq = seqs.get(n);
int index = delegateSequences.indexOf(seq);
// break if we are at bottom
if(index >= delegateSequences.size() - 1){
break;
}
Sequence previous = (Sequence) delegateSequences.set(index + 1, seq);
delegateSequences.set(index, previous);
}
logger.info("seqs.size()" + seqs.size());
if(seqs.size() > 0){
fireSequencesChangedAll();
}
}
public void writeSelectionAsFasta(Writer out) {
List <Sequence> selectedSequences = selectionModel.getSelectedSequences();
for(Sequence sequence : selectedSequences){
String tempSeq = sequence.getSelectedBasesAsString();
try {
//TODO maybe format fasta better
out.append(">");
out.append(sequence.getName());
out.append(LF);
out.append(sequence.getSelectedBasesAsString());
out.append(LF);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//logger.info("WroteSeq=" + n);
}
logger.info("Write done");
}
public void writeSelectedSequencesAsFasta(Writer out) {
writeSelectedSequencesAsFasta(out, false);
}
public void writeSelectedSequencesAsFasta(Writer out, boolean useIDAsName) {
List <Sequence> selectedSequences = selectionModel.getSelectedSequences();
for(Sequence sequence : selectedSequences){
logger.info("has sel");
writeSequenceAsFasta(sequence,out, useIDAsName);
}
logger.info("Write done");
}
private void writeSequenceAsFasta(Sequence sequence, Writer out, boolean useIDAsName){
try {
//TODO maybe format fasta better
out.append(">");
if(useIDAsName){
out.append(Integer.toString(sequence.getID()));
}else{
out.append(sequence.getName());
}
out.append(LF);
sequence.writeBases(out);
out.append(LF);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void writeUnSelectedSequencesAsFasta(Writer out) {
writeUnSelectedSequencesAsFasta(out, false);
}
public void writeUnSelectedSequencesAsFasta(Writer out, boolean useIDAsName) {
List <Sequence> unSelectedSequences = selectionModel.getUnSelectedSequences();
for(Sequence sequence : unSelectedSequences){
writeSequenceAsFasta(sequence, out, useIDAsName);
}
logger.info("Write done");
}
public byte getBaseAt(int x, int y) {
return delegateSequences.get(y).getBaseAtPos(x);
}
public AminoAcid getTranslatedAminoAcidAtNucleotidePos(int x, int y) {
return delegateSequences.get(y).getTranslatedAminoAcidAtNucleotidePos(x);
}
public int getLengthAt(int y) {
return delegateSequences.get(y).getLength();
}
public int indexOf(Sequence seq) {
return delegateSequences.indexOf(seq);
}
public FindObject findAndSelect(FindObject findObject) {
if(isTranslated || getSequenceType() == SequenceUtils.TYPE_AMINO_ACID){
return findAndSelectInAASequences(findObject);
}
else{
return findAndSelectInNucleotideSequences(findObject);
}
}
public FindObject findAndSelectALLInAASequences(FindObject findObj){
String regex = findObj.getRegexSearchTerm();
// Identical for AA and NUC search
Pattern pattern = Pattern.compile(regex,Pattern.CASE_INSENSITIVE);
findObj.setIsFound(false);
for(int n = findObj.getNextFindSeqNumber(); n < this.getSize(); n++){
Sequence seq = delegateSequences.get(n);
Interval foundPos = seq.find(pattern, findObj.getNextFindStartPos());
if(foundPos != null){
selectionModel.selectBases(seq, foundPos);
findObj.setNextFindSeqNumber(n);
// make sure it is not out of index
findObj.setNextFindStartPos(Math.min(foundPos.getStartPos() + 1, getLongestSequenceLength() - 1));
findObj.setFoundPos(foundPos.getStartPos(),n);
findObj.setIsFound(true);
// without this one it will find all positions
//return findObj;
}
// not found in this seq - start again from 0
findObj.setNextFindStartPos(0);
}
findObj.setNextFindSeqNumber(0);
findObj.setNextFindStartPos(0);
//findObj.setIsFound(false);
return findObj;
}
public FindObject findAndSelectInAASequences(FindObject findObj){
String regex = findObj.getRegexSearchTerm();
// Identical for AA and NUC search
Pattern pattern = Pattern.compile(regex,Pattern.CASE_INSENSITIVE);
findObj.setIsFound(false);
for(int n = findObj.getNextFindSeqNumber(); n < this.getSize(); n++){
Sequence seq = delegateSequences.get(n);
Interval foundPos = seq.find(pattern, findObj.getNextFindStartPos());
if(foundPos != null){
selectionModel.selectBases(seq, foundPos);
findObj.setNextFindSeqNumber(n);
// make sure it is not out of index
findObj.setNextFindStartPos(Math.min(foundPos.getStartPos() + 1, getLongestSequenceLength() - 1));
findObj.setFoundPos(foundPos.getStartPos(),n);
findObj.setIsFound(true);
// without this one it will find all positions
return findObj;
}
// not found in this seq - start again from 0
findObj.setNextFindStartPos(0);
}
findObj.setNextFindSeqNumber(0);
findObj.setNextFindStartPos(0);
return findObj;
}
public FindObject findAndSelectInNucleotideSequences(FindObject findObj) {
String regex = findObj.getRegexSearchTerm();
// lower-case before replace
regex = regex.toLowerCase();
// upac-codes
regex = regex.replaceAll("w", "\\[tua\\]");
regex = regex.replaceAll("n", "\\[agctu\\]");
regex = regex.replaceAll("r", "\\[ag\\]");
regex = regex.replaceAll("y", "\\[ctu\\]");
regex = regex.replaceAll("m", "\\[ca\\]");
regex = regex.replaceAll("k", "\\[tug\\]");
regex = regex.replaceAll("s", "\\[cg\\]");
regex = regex.replaceAll("b", "\\[ctug\\]");
regex = regex.replaceAll("d", "\\[atug\\]");
regex = regex.replaceAll("h", "\\[atuc\\]");
regex = regex.replaceAll("v", "\\[acg\\]");
regex = regex.replaceAll("n", "\\[agctu\\]");
// Identical for AA and NUC search
Pattern pattern = Pattern.compile(regex,Pattern.CASE_INSENSITIVE);
logger.info("startpos = " + findObj.getNextFindStartPos());
findObj.setIsFound(false);
for(int n = findObj.getNextFindSeqNumber(); n < this.getSize(); n++){
Sequence seq = delegateSequences.get(n);
Interval foundPos = seq.find(pattern, findObj.getNextFindStartPos());
if(foundPos != null){
selectionModel.selectBases(seq, foundPos);
findObj.setNextFindSeqNumber(n);
// make sure it is not out of index
findObj.setNextFindStartPos(Math.min(foundPos.getStartPos() + 1, getLongestSequenceLength() - 1));
findObj.setFoundPos(foundPos.getStartPos(),n);
findObj.setIsFound(true);
// without this one it will find all positions
return findObj;
}
// not found in this seq - start again from 0
findObj.setNextFindSeqNumber(n + 1);
findObj.setNextFindStartPos(0);
}
logger.info("beforereset = " + findObj.getNextFindStartPos());
// nothing found reset everything
findObj.setNextFindSeqNumber(0);
findObj.setNextFindStartPos(0);
return findObj;
}
public FindObject findInNames(FindObject findObj) {
String uCaseSearchTerm = findObj.getSearchTerm().toUpperCase();
if(findObj.isFindAll()){
// find all clear all previous
findObj.clearIndices();
for(int n = 0; n < delegateSequences.size(); n++){
Sequence seq = delegateSequences.get(n);
if(seq.getName().toUpperCase().indexOf(uCaseSearchTerm) > -1){
logger.info("Found" + n);
findObj.addFoundNameIndex(n);
findObj.setIsFound(true);
}
}
// Find single one
}else{
int startIndex = findObj.getNextNameFindIndex();
if(startIndex >= delegateSequences.size()){
startIndex = 0;
}
// only one to be found clear all previous
findObj.clearIndices();
for(int n = startIndex; n < delegateSequences.size(); n++){
Sequence seq = delegateSequences.get(n);
if(seq.getName().toUpperCase().indexOf(uCaseSearchTerm) > -1){
logger.info("Found" + n);
findObj.setFoundNameIndex(n);
findObj.setIsFound(true);
return findObj;
}
}
}
return findObj;
}
public boolean isEditable(){
return true;
}
public List<Sequence> insertGapRightOfSelectedBase(boolean undoable) {
List<Sequence> editedSequences = new ArrayList<Sequence>();
List<Sequence> selectedSeqs = selectionModel.getSelectedSequences();
Rectangle selectionBounds = selectionModel.getSelectionBounds();
for(Sequence seq: selectedSeqs){
if(undoable){
editedSequences.add(seq.getCopy());
}
seq.insertGapRightOfSelectedBase();
}
if(selectedSeqs.size() == delegateSequences.size()){
int posToAdd = (int) (selectionBounds.getBounds().getMaxX() + 1);
getAlignmentMeta().insertPosition(posToAdd);
}
if(selectedSeqs.size() > 0){
fireSequencesChanged(selectedSeqs);
}
return editedSequences;
}
public List<Sequence> insertGapLeftOfSelectedBase(boolean undoable) {
List<Sequence> editedSequences = new ArrayList<Sequence>();
List<Sequence> selectedSeqs = selectionModel.getSelectedSequences();
Rectangle selectionBounds = selectionModel.getSelectionBounds();
for(Sequence seq: selectedSeqs){
if(undoable){
Sequence copy = seq.getCopy();
editedSequences.add(copy);
}
seq.insertGapLeftOfSelectedBase();
}
if(selectedSeqs.size() > 0 && selectedSeqs.size() == delegateSequences.size()){
int posToAdd = (int) (selectionBounds.getBounds().getMaxX() - 1);
getAlignmentMeta().insertPosition(posToAdd);
}
if(selectedSeqs.size() > 0){
fireSequencesChanged(selectedSeqs);
}
return editedSequences;
}
public List<Sequence> deleteGapMoveLeft(boolean undoable) {
boolean gapPresentInAll = true;
List<Sequence> editedSequences = new ArrayList<Sequence>();
List<Sequence> selectedSeqs = selectionModel.getSelectedSequences();
Rectangle selectionBounds = selectionModel.getSelectionBounds();
for(Sequence seq: selectedSeqs){
//logger.info("hassel" + seq);
if(! seq.isGapLeftOfSelection()){
gapPresentInAll = false;
break;
}
}
if(gapPresentInAll){
selectedSeqs = selectionModel.getSelectedSequences();
for(Sequence seq: selectedSeqs){
if(undoable){
editedSequences.add(seq.getCopy());
}
seq.deleteGapLeftOfSelection();
}
}
if(gapPresentInAll && selectedSeqs.size() == delegateSequences.size()){
int posToDelete = (int) (selectionBounds.getBounds().getMaxX() - 1);
getAlignmentMeta().deletePosition(posToDelete);
}
if(gapPresentInAll){
fireSequencesChanged(selectedSeqs);
}
return editedSequences;
}
public List<Sequence> deleteGapMoveRight(boolean undoable) {
boolean gapPresentInAll = true;
List<Sequence> editedSequences = new ArrayList<Sequence>();
List<Sequence> selectedSeqs = selectionModel.getSelectedSequences();
Rectangle selectionBounds = selectionModel.getSelectionBounds();
for(Sequence seq: selectedSeqs){
//logger.info("hassel" + seq);
if(! seq.isGapRightOfSelection()){
gapPresentInAll = false;
break;
}
}
if(gapPresentInAll){
selectedSeqs = selectionModel.getSelectedSequences();
for(Sequence seq: selectedSeqs){
if(undoable){
editedSequences.add(seq.getCopy());
}
seq.deleteGapRightOfSelection();
}
}
if(gapPresentInAll && selectedSeqs.size() == delegateSequences.size()){
int posToDelete = (int) (selectionBounds.getBounds().getMaxX() + 1);
getAlignmentMeta().deletePosition(posToDelete);
}
if(gapPresentInAll){
fireSequencesChanged(selectedSeqs);
}
return editedSequences;
}
public boolean isGapPresentRightOfSelection() {
boolean gapPresentInAll = true;
List<Sequence> selectedSeqs = selectionModel.getSelectedSequences();
for(Sequence seq: selectedSeqs){
if(! seq.isGapRightOfSelection()){
gapPresentInAll = false;
break;
}
}
return gapPresentInAll;
}
public boolean isGapOrEndPresentRightOfSelection() {
boolean gapOrEndPresentInAll = true;
List<Sequence> selectedSeqs = selectionModel.getSelectedSequences();
for(Sequence seq: selectedSeqs){
if(! seq.isGapOrEndRightOfSelection()){
gapOrEndPresentInAll = false;
break;
}
}
return gapOrEndPresentInAll;
}
public List<Sequence> moveSelectedResiduesRightIfGapIsPresent(boolean undoable) {
Rectangle oldSelectRectangle = selectionModel.getSelectionBounds();
List<Sequence> editedSequences = new ArrayList<Sequence>();
boolean wasEndRightOfSelection = false;
if(isGapOrEndPresentRightOfSelection()){
List<Sequence> selectedSeqs = selectionModel.getSelectedSequences();
for(Sequence seq: selectedSeqs){
if(undoable){
editedSequences.add(seq.getCopy());
}
if(seq.isEndRightOfSelection()){
wasEndRightOfSelection = true;
}
seq.moveSelectedResiduesRightIfGapOrEndIsPresent();
}
}
Rectangle newSelect = selectionModel.getSelectionBounds();
if(oldSelectRectangle == null){
// nothing can have changed
}else{
newSelect.add(oldSelectRectangle);
fireSequencesChanged(newSelect);
if(wasEndRightOfSelection){
rightPadWithGapUntilEqualLength();
}
}
return editedSequences;
}
public boolean isGapPresentLeftOfSelection() {
boolean gapPresentInAll = true;
List<Sequence> selectedSeqs = selectionModel.getSelectedSequences();
for(Sequence seq: selectedSeqs){
if(! seq.isGapLeftOfSelection()){
gapPresentInAll = false;
break;
}
}
return gapPresentInAll;
}
public List<Sequence> moveSelectedResiduesLeftIfGapIsPresent(boolean undoable){
Rectangle oldSelectRectangle = selectionModel.getSelectionBounds();
List<Sequence> editedSequences = new ArrayList<Sequence>();
if(isGapPresentLeftOfSelection()){
List<Sequence> selectedSeqs = selectionModel.getSelectedSequences();
for(Sequence seq: selectedSeqs){
if(undoable){
editedSequences.add(seq.getCopy());
}
seq.moveSelectedResiduesLeftIfGapIsPresent();
}
}
Rectangle newSelect = selectionModel.getSelectionBounds();
if(oldSelectRectangle == null){
// nothing can have changed
}else{
newSelect.add(oldSelectRectangle);
fireSequencesChanged(newSelect);
}
return editedSequences;
}
// TODO break this into two (is gap present) and move
public List<Sequence> moveSelectedResiduesIfGapIsPresent(int diff, boolean undoable){
List<Sequence> editedSequences = new ArrayList<Sequence>();
// TODO this is moving and remembering startpos
int unmovedDiff = diff - selectionOffset;
if(unmovedDiff < 0){
int absDiff = Math.abs(unmovedDiff);
int n = 0;
while(n < absDiff){
// only keep first one
if(editedSequences == null){
editedSequences = this.moveSelectedResiduesLeftIfGapIsPresent(undoable);
}else{
this.moveSelectedResiduesLeftIfGapIsPresent(undoable);
}
n++;
}
}
if(unmovedDiff > 0){
int absDiff = Math.abs(unmovedDiff);
int n = 0;
while(n < absDiff){
// only keep first one
if(editedSequences == null){
editedSequences = this.moveSelectedResiduesRightIfGapIsPresent(undoable);
}else{
this.moveSelectedResiduesRightIfGapIsPresent(undoable);
}
n++;
}
}
selectionOffset = diff;
return editedSequences;
}
public void realignNucleotidesUseTheseAASequenceAsTemplate(AlignmentListModel templateSeqs) throws Exception{
// make sure this alignment is translated
setTranslation(true);
for(Sequence templateSeq: templateSeqs.getDelegateSequencesCopy()){
// do partial name since it if being cut by some programs....
Sequence nucSeq = this.getSequenceByName(templateSeq.getName());
logger.info("nucSeq=" + nucSeq.getName() + nucSeq.getBasesAsString());
logger.info("templateSeq=" + templateSeq.getName() + templateSeq.getBasesAsString());
realignNucleotidesUseThisAASequenceAsTemplate(nucSeq, templateSeq);
}
// show result as nucleotides
setTranslation(false);
fireSequencesChangedAll();
}
private void realignNucleotidesUseThisAASequenceAsTemplate(Sequence nucSeq, Sequence template) throws Exception {
if(nucSeq instanceof InMemorySequence){
StringBuilder newSeq = new StringBuilder(nucSeq.getLength());
int nextFindStartPos = 0;
for(int n = 0; n < template.getLength(); n++){
byte nextAAByte = template.getBaseAtPos(n);
AminoAcid aaTemplate = AminoAcid.getAminoAcidFromByte(nextAAByte);
// logger.info("aaTemplate.getCodeCharVal()" + aaTemplate.getCodeCharVal());
if(aaTemplate.getCodeCharVal() == AminoAcid.GAP.getCodeCharVal()){
newSeq.append((char)SequenceUtils.GAP_SYMBOL);
newSeq.append((char)SequenceUtils.GAP_SYMBOL);
newSeq.append((char)SequenceUtils.GAP_SYMBOL);
}else{
// logger.info("search for " + aaTemplate.getCodeCharVal() + " in seq " + nucSeq.getName() + " from pos " + nextFindStartPos);
int posFound = nucSeq.find(aaTemplate.getCodeByteVal(), nextFindStartPos);
if(posFound == -1){
logger.info("posnotfound");
throw new Exception("Alignments not matching-exception, when trying to align sequences");
}
byte[] nextNucs = nucSeq.getGapPaddedCodonInTranslatedPos(posFound);
newSeq.append(new String(nextNucs));
nextFindStartPos = posFound + 1;
}
}
logger.info("newSeq.length()" + newSeq.length());
((InMemorySequence) nucSeq).setBases(newSeq.toString().getBytes());
fireSequencesChanged(nucSeq);
}
}
/*
private void realignNucleotidesUseThisAASequenceAsTemplate(Sequence nucSeq, Sequence template) throws Exception {
StringBuilder newSeq = new StringBuilder(nucSeq.getLength());
int nextPos = 0;
for(int n = 0; n < template.getLength(); n++){
byte nextAAByte = template.getBaseAtPos(n);
AminoAcid aaTemplate = AminoAcid.getAminoAcidFromByte(nextAAByte);
if(aaTemplate.getCodeCharVal() == AminoAcid.GAP.getCodeCharVal()){
newSeq.append((char)SequenceUtils.GAP_SYMBOL);
newSeq.append((char)SequenceUtils.GAP_SYMBOL);
newSeq.append((char)SequenceUtils.GAP_SYMBOL);
}else{
byte nextByte = nucSeq.getBaseAtPos(n);
AminoAcid translated = AminoAcid.getAminoAcidFromByte(nextByte);
if(translated != aaTemplate){
logger.info("posnotfound");
// System.out.println("");
// throw new Exception("Alignments not matching-exception, when trying to align sequences");
}
}
}
}
*/
public Sequence getSequenceByName(String name) {
if(name == null){
return null;
}
Sequence foundSeq = null;
for(Sequence seq: delegateSequences){
if(name.equalsIgnoreCase(seq.getName())){
foundSeq = seq;
break;
}
}
return foundSeq;
}
public ArrayList<Sequence> getSequencesByName(String name) {
ArrayList<Sequence> foundSeqs = new ArrayList<Sequence>();
if(name == null){
return foundSeqs;
}
for(Sequence seq: delegateSequences){
if(name.equalsIgnoreCase(seq.getName())){
foundSeqs.add(seq);
}
}
return foundSeqs;
}
public void deleteAllGaps() {
for(Sequence seq: delegateSequences){
seq.deleteAllGaps();
}
fireSequencesChangedAll();
}
public boolean rightPadWithGapUntilEqualLength(){
int longLen = getLongestSequenceLength();
ArrayList<Sequence> paddedSeqs = new ArrayList<Sequence>();
for(Sequence sequence : delegateSequences){
if(sequence.getLength() < longLen){
sequence.rightPadSequenceWithGaps(longLen);
paddedSeqs.add(sequence);
}
}
if(paddedSeqs.size() > 0){
fireSequencesChanged(paddedSeqs);
return true;
}
else{
return false;
}
}
public boolean leftPadWithGapUntilEqualLength() {
int longLen = getLongestSequenceLength();
ArrayList<Sequence> paddedSeqs = new ArrayList<Sequence>();
for(Sequence sequence : delegateSequences){
if(sequence.getLength() < longLen){
sequence.leftPadSequenceWithGaps(longLen);
paddedSeqs.add(sequence);
}
}
if(paddedSeqs.size() > 0){
fireSequencesChanged(paddedSeqs);
return true;
}
else{
return false;
}
}
/*
public void rightTrimSequencesRemoveGapsUntilEqualLength(){
for(int n = sequences.size() - 1 && thisPosHasBase == false; n >=0; n--){
boolean thisPosHasBase = false;
for(Sequence sequence : sequences){
if(n < sequence.getLength() && !NucleotideUtilities.isGap(sequence.getBaseAtPos(n)){
thisPosHasBase = true;
break;
}
}
if(thisPosHasBase == false){
for(Sequence sequence : sequences){
if(n < sequence.getLength() && !NucleotideUtilities.isGap(sequence.getBaseAtPos(n)){
thisPosHasBase = true;
break;
}
}
}
else{
break;
}
}
}
*/
public boolean rightTrimSequencesRemoveGapsUntilEqualLength(){
boolean wasTrimmed = false;
String cons = getConsensus();
logger.info("cons=" + cons);
// check if there are any
if(cons.indexOf(SequenceUtils.GAP_SYMBOL) > 0){
// create a bit-mask with pos to delete
boolean[] deleteMask = new boolean[cons.length()];
for(int n = deleteMask.length - 1; n>=0 ;n--){
if(cons.charAt(n) == SequenceUtils.GAP_SYMBOL){
deleteMask[n] = true;
}else{
break; // break out of for loop
}
}
if(ArrayUtils.contains(deleteMask, true)){
deleteBasesInAllSequencesFromMask(deleteMask);
wasTrimmed = true;
}
}
if(wasTrimmed){
fireSequencesChangedAll();
}
return wasTrimmed;
}
public void deleteBasesInAllSequencesFromMask(boolean[] deleteMask) {
for(Sequence sequence : delegateSequences){
sequence.deleteBasesFromMask(deleteMask);
}
fireSequencesChangedAll();
}
public String getConsensus() {
if(isTranslated || getSequenceType() == SequenceUtils.TYPE_AMINO_ACID){
return getAminoAcidConsensus();
}
else{
return getNucleotideConsensus();
}
}
private String getAminoAcidConsensus() {
byte[] consVals = new byte[getLongestSequenceLength()];
Arrays.fill(consVals, AminoAcid.GAP.getCodeByteVal());
for(Sequence sequence : delegateSequences){
for(int n = 0; n < sequence.getLength(); n++){
consVals[n] = AminoAcid.getConsensusFromByteVal(sequence.getBaseAtPos(n), (byte)consVals[n]);
}
}
String consAsString = new String(consVals);
logger.info(consAsString);
return consAsString;
}
private String getNucleotideConsensus(){
int[] consVals = new int[getLongestSequenceLength()];
for(Sequence sequence : delegateSequences){
int[] baseVals = sequence.getSequenceAsBaseVals();
// bitwise add everything
for(int n = 0; n < baseVals.length; n++){
consVals[n] = consVals[n] | baseVals[n];
}
}
char[] cons = new char[consVals.length];
for(int n = 0; n < cons.length; n++){
cons[n] = NucleotideUtilities.charFromBaseVal(consVals[n]);
}
return new String(cons);
}
public void reverseComplement() {
for(Sequence seq : delegateSequences){
seq.reverseComplement();
}
fireSequencesChangedAll();
}
public void reverseComplementFullySelectedSequences() {
List<Sequence> fullySelected = getFullySelectedSequences();
for(Sequence seq : fullySelected){
seq.reverseComplement();
}
if(fullySelected.size() > 0){
fireSequencesChanged(fullySelected);
}
}
public void complement() {
for(Sequence seq : delegateSequences){
seq.complement();
}
fireSequencesChangedAll();
}
public int getLongestSequenceName() {
long startTime = System.currentTimeMillis();
if(cachedLongestSequenceName <= 0){
// this is double locked to avoid synchronized block after the cached initialization of variacle
// cached variable has to be declared volatile above
// see: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html and http://en.wikipedia.org/wiki/Double-checked_locking
synchronized(this){
if(cachedLongestSequenceName <=0){
int maxlen = 0;
for(Sequence seq: delegateSequences){
maxlen = Math.max(maxlen, seq.getName().length());
}
cachedLongestSequenceName = maxlen;
}
}
}
long endTime = System.currentTimeMillis();
logger.info("getLongestSequenceName took " + (endTime - startTime) + " milliseconds");
return cachedLongestSequenceName;
}
public boolean isPositionValid(int x, int y) {
return rangeCheck(x,y);
}
public boolean rangeCheck(int x, int y){
boolean isValid = false;
if(y > -1 && y < delegateSequences.size()){
if(x >= 0 && x < delegateSequences.get(y).getLength()){
isValid = true;
}
}
return isValid;
}
private boolean rangeCheck(Point point) {
return rangeCheck(point.x, point.y);
}
public Sequence getSequenceByID(int id){
for(Sequence seq: delegateSequences){
if(seq.getID() == id){
return seq;
}
}
return null;
}
public void sortSequencesByName() {
//logger.info(sequences);
Collections.sort(delegateSequences);
fireSequencesOrderChangedAll();
}
public void sortSequencesByCharInSelectedColumn(AliHistogram histogram) {
// get first selected column
Point selPos = selectionModel.getFirstSelectedPos();
Collections.sort(delegateSequences, new SequencePositionComparator(selPos.x, getHistogram()));
fireSequencesOrderChangedAll();
}
public AliHistogram getHistogram(){
if(cachedHistogram == null){
// this is double locked to avoid synchronized block after the lazy initialization of Histogram object
// Histogram has to be declared volatile above
// see: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html and http://en.wikipedia.org/wiki/Double-checked_locking
synchronized(this){
if(cachedHistogram == null){
cachedHistogram = createHistogram();
}
}
}
return cachedHistogram;
}
private AliHistogram createHistogram(){
long startTime = System.currentTimeMillis();
AliHistogram histogram = null;
if(sequenceType == SequenceUtils.TYPE_AMINO_ACID || isTranslated){
histogram = new AAHistogram(getLongestSequenceLength());
}else{
histogram = new NucleotideHistogram(getLongestSequenceLength());
}
for(Sequence seq: delegateSequences){
if(sequenceType == SequenceUtils.TYPE_AMINO_ACID || isTranslated){
histogram.addSequence(seq);
}else{
histogram.addSequence(seq);
}
}
long endTime = System.currentTimeMillis();
logger.info("Create histogram took " + (endTime - startTime) + " milliseconds");
return histogram;
}
public List<Sequence> replaceSelectedCharactersWithThis(AlignmentListModel newOnes, boolean undoable) {
List<Sequence> editedSequences = new ArrayList<Sequence>();
List<Sequence> selectedSeqs = selectionModel.getSelectedSequences();
for(Sequence seq: selectedSeqs){
// No partial name that might swich sequences
//Sequence realignedSeq = newOnes.getSequenceByPartialName(seq.getName());
Sequence realignedSeq = newOnes.getSequenceByName(seq.getName());
byte[] realignedBases;
// muscle removes empty seq if that is case create an empty bases
if(realignedSeq != null){
realignedBases = realignedSeq.getAllBasesAsByteArray();
}else{
realignedBases = SequenceUtils.createGapByteArray(newOnes.getLongestSequenceLength());
}
int selPos[] = seq.getSelectedPositions();
// if selection is larger than result pad up with gap
if(selPos.length > realignedBases.length){
byte[] paddedRealigned = Arrays.copyOf(realignedBases, selPos.length);
for(int n = realignedBases.length; n < paddedRealigned.length; n++){
paddedRealigned[n] = SequenceUtils.GAP_SYMBOL;
}
realignedBases = paddedRealigned;
}
seq.replaceBases(selPos[0],selPos[selPos.length -1],realignedBases);
seq.setSelection(selPos[0],selPos[0] + realignedBases.length -1, false);
if(undoable){
editedSequences.add(seq.getCopy());
}
}
if(selectedSeqs.size() > 0){
Rectangle bounds = selectionModel.getSelectionBounds();
fireSequencesChanged(bounds);
}
return editedSequences;
}
public boolean mergeTwoSequences(InMemorySequence seq1, InMemorySequence seq2, boolean allowOverlap){
if(sequenceType == SequenceUtils.TYPE_NUCLEIC_ACID){
return mergeTwoNucleotideSequences(seq1, seq2, allowOverlap);
}
else{
return mergeTwoAminoAcidSequences(seq1, seq2, allowOverlap);
}
}
public boolean mergeTwoAminoAcidSequences(InMemorySequence seq1, InMemorySequence seq2, boolean allowOverlap){
boolean isMerged = false;
int nExactOverlap = SequenceUtils.countExactAminoAcidOverlap(seq1, seq2);
int nDifferentOverlap = SequenceUtils.countDifferentAminoAcidOverlap(seq1, seq2);
boolean isOverlap = false;
boolean isOverlapExactlySame = false;
if(nExactOverlap > 0 || nDifferentOverlap > 0){
isOverlap = true;
if(nExactOverlap > 0 && nDifferentOverlap ==0){
isOverlapExactlySame = true;
}
}
// Warn
if(isOverlap){
String overlapMessage = "";
if(isOverlapExactlySame){
overlapMessage = "Overlapping parts are identical (" + nExactOverlap +"bases)";
}else{
overlapMessage = "Overlapping parts are different (" + nDifferentOverlap + "/" + (nDifferentOverlap + nExactOverlap) + ")";
overlapMessage += LF + "Differences will be replaced by " + AminoAcid.X.getCodeCharVal();
}
String message="Sequences are overlapping - " + overlapMessage + LF +
"Do you want to continue?";
// TODO I dont know how to deal with dialogs in a nice pattern way if it is within the alignment class? Maybe it should be splited
// and moved into aliview-class (for example to do a temporary merge and then ask and then call alignment again to do join
int retVal = JOptionPane.showConfirmDialog(DialogUtils.getDialogParent(), message, "Continue?", JOptionPane.OK_CANCEL_OPTION);
if(retVal != JOptionPane.OK_OPTION){
return false;
}
}
else{
String message= "Sequences are NOT overlapping" + LF +
"Do you want to continue?";
int retVal = JOptionPane.showConfirmDialog(DialogUtils.getDialogParent(), message, "Continue?", JOptionPane.OK_CANCEL_OPTION);
if(retVal != JOptionPane.OK_OPTION){
return false;
}
}
//
// OK go ahead merge
//
byte[] merged = new byte[seq1.getLength()];
for(int n = 0; n < seq1.getLength(); n++){
merged[n] = AminoAcid.getConsensusFromByteVal(seq1.getBaseAtPos(n),seq2.getBaseAtPos(n));
}
if(isOverlap && allowOverlap == false){
// skip
}
else{
// set new merged data - keep selection
seq1.setBases(merged);
seq1.setName(seq1.getName() + "_merged_" + seq2.getName());
seq2.setBases(merged.clone());
seq2.setName(seq2.getName() + "_merged_" + seq1.getName());
isMerged = true;
}
if(isMerged){
List<Sequence> mergedSeqs = new ArrayList<Sequence>(2);
mergedSeqs.add(seq1);
mergedSeqs.add(seq2);
fireSequencesChanged(mergedSeqs);
}
return isMerged;
}
public boolean mergeTwoNucleotideSequences(InMemorySequence seq1, InMemorySequence seq2, boolean allowOverlap){
boolean isMerged = false;
int nExactOverlap = SequenceUtils.countExactNucleotideOverlap(seq1, seq2);
int nDifferentOverlap = SequenceUtils.countDifferentNucleotideOverlap(seq1, seq2);
boolean isOverlap = false;
boolean isOverlapExactlySame = false;
if(nExactOverlap > 0 || nDifferentOverlap > 0){
isOverlap = true;
if(nExactOverlap > 0 && nDifferentOverlap ==0){
isOverlapExactlySame = true;
}
}
// Warn
if(isOverlap){
String overlapMessage = "";
if(isOverlapExactlySame){
overlapMessage = "Overlapping parts are identical (" + nExactOverlap +"bases)";
}else{
overlapMessage = "Overlapping parts are different (" + nDifferentOverlap + "/" + (nDifferentOverlap + nExactOverlap) + ")";
}
String message="Sequences are overlapping - " + overlapMessage + LF +
"Do you want to continue?";
// TODO I dont know how to deal with dialogs in a nice pattern way if it is within the alignment class? Maybe it should be splited
// and moved into aliview-class (for example to do a temporary merge and then ask and then call alignment again to do join
int retVal = JOptionPane.showConfirmDialog(DialogUtils.getDialogParent(), message, "Continue?", JOptionPane.OK_CANCEL_OPTION);
if(retVal != JOptionPane.OK_OPTION){
return false;
}
}
else{
String message= "Sequences are NOT overlapping" + LF +
"Do you want to continue?";
int retVal = JOptionPane.showConfirmDialog(DialogUtils.getDialogParent(), message, "Continue?", JOptionPane.OK_CANCEL_OPTION);
if(retVal != JOptionPane.OK_OPTION){
return false;
}
}
//
// OK go ahead merge
//
byte[] merged = new byte[seq1.getLength()];
for(int n = 0; n < seq1.getLength(); n++){
merged[n] = NucleotideUtilities.getConsensusFromBases(seq1.getBaseAtPos(n),seq2.getBaseAtPos(n));
}
if(isOverlap && allowOverlap == false){
// skip
}
else{
// set new merged data - keep selection
seq1.setBases(merged);
seq1.setName(seq1.getName() + "_merged_" + seq2.getName());
seq2.setBases(merged.clone());
seq2.setName(seq2.getName() + "_merged_" + seq1.getName());
isMerged = true;
}
if(isMerged){
List<Sequence> mergedSeqs = new ArrayList<Sequence>(2);
mergedSeqs.add(seq1);
mergedSeqs.add(seq2);
fireSequencesChanged(mergedSeqs);
}
return isMerged;
}
public ArrayList<Sequence> findDuplicates(){
HashSet<Sequence> dupeSequences = new HashSet<Sequence>();
for(int n = 0; n < delegateSequences.size(); n++){
Sequence testSeq = delegateSequences.get(n);
boolean isDupe = false;
for(int m = n + 1; m < delegateSequences.size(); m++){
Sequence otherSeq = delegateSequences.get(m);
if(testSeq.getLength() == otherSeq.getLength()){
if(SequenceUtils.isSeqResiduesIdentical(testSeq, otherSeq)){
// add the dupes (since it is a set they will only be added once
dupeSequences.add(testSeq);
dupeSequences.add(otherSeq);
}
}
else{
logger.debug("wrong len");
}
}
logger.info("dupeSequences.size()" + dupeSequences.size());
}
ArrayList<Sequence> dupeList = new ArrayList<Sequence>(dupeSequences);
return dupeList;
}
/*
public AliHistogram getTranslatedHistogram() {
if(cachedTranslatedHistogram == null){
cachedTranslatedHistogram = createTranslatedHistogram();
}
return cachedTranslatedHistogram;
}
private AliHistogram createTranslatedHistogram() {
long startTime = System.currentTimeMillis();
boolean wasTranslated = isTranslated;
setTranslation(true);
AAHistogram histogram = new AAHistogram(getLongestSequenceLength());
for(Sequence seq: delegateSequences){
for(int n = 0; n < seq.getLength(); n++){
histogram.addAminoAcid(n,AminoAcid.getAminoAcidFromByte(seq.getBaseAtPos(n)));
}
}
if(wasTranslated == false){
setTranslation(false);
}
long endTime = System.currentTimeMillis();
logger.info("Create translated histogram took " + (endTime - startTime) + " milliseconds");
return histogram;
}
*/
public int size() {
return delegateSequences.size();
}
public void sortSequencesByThisModel(AlignmentListModel prevSeqOrder){
ArrayList<Sequence> seqsInOrder = new ArrayList<Sequence>(prevSeqOrder.size());
for(int n = 0; n < prevSeqOrder.getSize(); n++){
Sequence prev = prevSeqOrder.get(n);
//Sequence seq = getSequenceByPartialName(prev.getName());
Sequence seq = getSequenceByName(prev.getName());
seqsInOrder.add(seq);
}
if(seqsInOrder.size() == delegateSequences.size()){
setSequences(seqsInOrder);
}
fireSequencesChangedAll();
}
public ArrayList<String> findDuplicateNames() {
ArrayList<String> dupes = new ArrayList<String>();
HashSet set = new HashSet<String>(delegateSequences.size());
for(Sequence seq: delegateSequences){
boolean isNotDuplicate = set.add(seq.getName());
if(isNotDuplicate == false){
dupes.add(seq.getName());
}
}
return dupes;
}
//
// DO something with selected sequences
//
public List<Sequence> replaceSelectedBasesWithGap(boolean undoable) {
return replaceSelectedWithChar((char)SequenceUtils.GAP_SYMBOL, undoable);
}
public List<Sequence> replaceSelectedWithChar(char newChar, boolean undoable) {
List<Sequence> editedSequences = new ArrayList<Sequence>();
List<Sequence> selectedSeqs = selectionModel.getSelectedSequences();
boolean wasReplaced = false;
for(Sequence seq: selectedSeqs){
if(undoable){
editedSequences.add(seq.getCopy());
}
seq.replaceSelectedBasesWithChar(newChar);
wasReplaced = true;
}
if(wasReplaced){
fireSequencesChanged(selectionModel.getSelectionBounds());
}
return editedSequences;
}
/**
*
* TODO probably change this into two methods, getSelectedPositions and then deletePositions...
* @return
*/
public List<Sequence> deleteSelectedBases() {
List<Sequence> editedSequences = new ArrayList<Sequence>();
List<Sequence> selectedSeqs = selectionModel.getSelectedSequences();
for(Sequence seq: selectedSeqs){
editedSequences.add(seq);
seq.deleteSelectedBases();
}
if(editedSequences.size() > 0){
fireSequencesChangedAll();
}
return editedSequences;
}
//
// ****************** SELECTION
//
public ArrayList<Sequence> findAndSelectDuplicates(){
ArrayList<Sequence> dupes = findDuplicates();
selectionModel.selectSequences(dupes);
return dupes;
}
public void selectDuplicateNamesSequences() {
ArrayList<String> dupeNames = findDuplicateNames();
List<Sequence> dupeSeqs = new ArrayList<Sequence>();
for(String dupeName: dupeNames){
dupeSeqs.addAll(getSequencesByName(dupeName));
}
selectionModel.selectSequences(dupeSeqs);
}
public void selectEverythingWithinGaps(Point point) {
if(!rangeCheck(point)){
return;
}
// TODO this should probably be moved to out of listSelectionModel
selectionModel.selectAllBasesUntilGapInThisSequence(delegateSequences.get(point.y), point.x);
}
public void selectAll(CharSet aCharSet) {
List<Integer> columns = new ArrayList<Integer>();
for(int n = 0; n < getLongestSequenceLength(); n++){
if(aCharSet.contains(n)){
columns.add(new Integer(n));
}else{
// nothing to do
}
}
if(columns.size() > 0){
selectionModel.clearSequenceSelection();
selectionModel.selectColumns(columns);
}
}
public int getSelectedColumnCount() {
return selectionModel.getSelectedColumnCount();
}
public int getSelectedSequencesCount() {
return selectionModel.getSelectedSequencesCount();
}
public List<Sequence> getSelectedSequences() {
return selectionModel.getSelectedSequences();
}
public String getSelectionNames() {
return selectionModel.getSelectionNames();
}
public int getFirstSelectedWholeColumn() {
return selectionModel.getFirstSelectedWholeColumn();
}
public int getLastSelectedWholeColumn() {
return selectionModel.getLastSelectedWholeColumn();
}
//
// TODO domething about this one
//
public void setSelectionOffset(int i) {
this.selectionOffset = i;
}
public void expandSelectionDown() {
selectionModel.selectionExtendDown();
}
public String getSelectionAsNucleotides() {
return selectionModel.getSelectionAsNucleotides();
}
public int getFirstSelectedSequenceIndex(){
return selectionModel.getFirstSelectedSequenceIndex();
}
public int getLastSelectedSequenceIndex(){
return selectionModel.getLastSelectedSequenceIndex();
}
public String getFirstSelectedName() {
return selectionModel.getFirstSelectedName();
}
public List<Sequence> setFirstSelectedName(String newName) {
return selectionModel.setFirstSelectedName(newName);
}
public Sequence getFirstSelected() {
return selectionModel.getFirstSelected();
}
public boolean hasSelection() {
return selectionModel.hasSelection();
}
public void selectAll() {
selectionModel.selectAll();
}
public void selectionExtendRight() {
selectionModel.selectionExtendRight();
}
public void selectionExtendLeft() {
selectionModel.selectionExtendLeft();
}
public void invertSelection() {
selectionModel.invertSelection();
}
public void copySelectionFromInto(int indexFrom, int indexTo) {
selectionModel.copySelectionFromInto(indexFrom, indexTo);
}
public void selectColumn(int columnIndex) {
selectionModel.selectColumn(columnIndex);
}
public void clearColumnSelection(int columnIndex) {
selectionModel.clearColumnSelection(columnIndex);
}
public void copySelectionFromPosX1toX2(int x1, int x2) {
selectionModel.copySelectionFromPosX1toX2(x1, x2);
}
public Point getFirstSelectedPos() {
return selectionModel.getFirstSelectedPos();
}
public Point getLastSelectedPos() {
return selectionModel.getLastSelectedPos();
}
public Point getFirstSelectedUngapedPos() {
return selectionModel.getFirstSelectedUngapedPos();
}
public void setSelectionWithin(Rectangle bounds) {
selectionModel.setSelectionWithin(bounds);
}
public void selectSequencesByName(String name) {
ArrayList<Sequence> foundSeqs = getSequencesByName(name);
selectionModel.selectSequences(foundSeqs);
}
public ArrayList<Integer> getIndicesOfSequencesWithSelection() {
return selectionModel.getIndicesOfSequencesWithSelection();
}
public ArrayList<Integer> getIndicesOfSequencesWithAllSelected() {
return selectionModel.getIndicesOfSequencesWithAllSelected();
}
public void selectSequencesWithIndex(List<Integer> listVals){
selectionModel.selectSequencesWithIndex(listVals);
}
public void selectSequencesWithIndex(int[] selectedIndex){
selectionModel.selectSequencesWithIndex(selectedIndex);
}
public void clearSequenceSelection() {
selectionModel.clearSequenceSelection();
}
public void selectSequenceWithIndex(int y) {
selectionModel.selectSequenceWithIndex(y);
}
public boolean isBaseSelected(int x, int y) {
//logger.info("isBaseSelected" + sequences.get(y));
return selectionModel.isBaseSelected(x, y);
}
public void setSelectionAt(int xPos, int yPos, boolean clearFirst) {
selectionModel.setSelectionAt(xPos, yPos, clearFirst);
}
public long getSelectionSize(){
return selectionModel.getSelectionSize();
}
public boolean hasFullySelectedSequences() {
return selectionModel.hasFullySelectedSequences();
}
public AlignmentSelectionModel getAlignmentSelectionModel() {
return selectionModel;
}
public void setTempSelection(Rectangle selectRectMatrixCoords) {
selectionModel.setTempSelection(selectRectMatrixCoords);
}
public Rectangle getTempSelection() {
return selectionModel.getTempSelection();
}
public void clearTempSelection() {
selectionModel.clearTempSelection();
}
public void clearAllSelectionInSequenceWithIndex(int y) {
selectionModel.clearAllSelectionInSequenceWithIndex(y);
}
public void addAlignmentSelectionListener(AlignmentSelectionListener listener) {
selectionModel.addAlignmentSelectionListener(listener);
}
//
// ****************** END SELECTION
//
//
// ****************** SEQUENCES CHANGED EVENTS
//
private void fireSequencesChanged(Sequence seq) {
int index = delegateSequences.indexOf(seq);
fireSequencesChanged(index, index);
}
private void fireSequencesChanged(List<Sequence> seqs) {
int minIndex = delegateSequences.size();
int maxIndex = 0;
for(Sequence seq: seqs){
int index = delegateSequences.indexOf(seq);
minIndex = Math.min(index, minIndex);
maxIndex = Math.max(index, maxIndex);
}
fireSequencesChanged(minIndex, maxIndex);
}
private void fireSequencesChanged(int minIndex, int maxIndex) {
// TODO this might be a bit ugly...
cachedLongestSequenceLength = -1;
Rectangle rect = new Rectangle(0,minIndex, this.getLongestSequenceLength(), maxIndex + 1);
fireSequencesChanged(rect);
}
private void fireSequencesChanged(Rectangle rect) {
sequencesChanged(rect);
}
private void sequencesChanged(Rectangle rect) {
logger.info("sequencesChanged");
// clear cached values
cachedLongestSequenceName = -1;
cachedLongestSequenceLength = -1;
cachedHistogram = null;
Object[] listeners = listenerList.getListenerList();
AlignmentDataEvent e = null;
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == AlignmentDataListener.class) {
if (e == null) {
e = new AlignmentDataEvent(this, ListDataEvent.CONTENTS_CHANGED, rect);
}
((AlignmentDataListener)listeners[i+1]).contentsChanged(e);
}
else if (listeners[i] == ListDataListener.class) {
if (e == null) {
e = new AlignmentDataEvent(this, ListDataEvent.CONTENTS_CHANGED, rect);
}
((ListDataListener)listeners[i+1]).contentsChanged(e);
}
}
}
private void fireSequencesChangedAll() {
fireSequencesChanged(0, this.size() -1);
}
private void fireSequencesOrderChangedAll() {
fireSequencesChanged(0, this.size() -1);
}
private void fireSequencesChangedAllNew() {
fireSequencesChanged(0, this.size() -1);
}
private void fireSequenceIntervalRemoved(int index0, int index1){
fireSequencesChanged(index0, index1);
}
private void fireSequenceIntervalAdded(int index0, int index1) {
if(index0 < 0 || index1 < 0){
return;
}
fireSequencesChanged(index0, index1);
}
public void setTranslation(boolean shouldTrans) {
if(shouldTrans != isTranslated){
if(shouldTrans){
selectionModel.translateSelection(getAlignment().getAlignmentMeta());
}else{
selectionModel.reTranslateSelection(getAlignment().getAlignmentMeta());
}
isTranslated = shouldTrans;
fireSequencesChangedAll();
}
}
public AlignmentMeta getAlignmentMeta(){
if(getAlignment() != null){
return getAlignment().getAlignmentMeta();
}
return null;
}
private Alignment getAlignment() {
return this.alignment;
}
public boolean isTranslated() {
return isTranslated;
}
public Rectangle getSelectionBounds() {
return selectionModel.getSelectionBounds();
}
public void setAlignment(Alignment alignment) {
this.alignment = alignment;
}
}