*/
package jalview.analysis;
-import static jalview.io.gff.GffConstants.CLINICAL_SIGNIFICANCE;
-
+import jalview.commands.RemoveGapColCommand;
import jalview.datamodel.AlignedCodon;
import jalview.datamodel.AlignedCodonFrame;
import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping;
import jalview.datamodel.SequenceGroup;
import jalview.datamodel.SequenceI;
import jalview.datamodel.features.SequenceFeatures;
-import jalview.io.gff.Gff3Helper;
import jalview.io.gff.SequenceOntologyI;
import jalview.schemes.ResidueProperties;
import jalview.util.Comparison;
import jalview.util.IntRangeComparator;
import jalview.util.MapList;
import jalview.util.MappingUtils;
-import jalview.util.StringUtils;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
{
for (int ix = 0, nx = xrefs.size(); ix < nx; ix++)
{
- DBRefEntry xref = xrefs.get(ix);
+ DBRefEntry xref = xrefs.get(ix);
String xrefName = xref.getSource() + "|" + xref.getAccessionId();
// case-insensitive test, consistent with DBRefEntry.equalRef()
if (xrefName.equalsIgnoreCase(name))
cdsSeqs.add(cdsSeq);
- if (!dataset.getSequences().contains(cdsSeqDss))
- {
- // check if this sequence is a newly created one
- // so needs adding to the dataset
- dataset.addSequence(cdsSeqDss);
- }
-
/*
- * add a mapping from CDS to the (unchanged) mapped to range
+ * build the mapping from CDS to protein
*/
- List<int[]> cdsRange = Collections.singletonList(new int[] { 1,
- cdsSeq.getLength() });
+ List<int[]> cdsRange = Collections
+ .singletonList(new int[]
+ { cdsSeq.getStart(),
+ cdsSeq.getLength() + cdsSeq.getStart() - 1 });
MapList cdsToProteinMap = new MapList(cdsRange,
mapList.getToRanges(), mapList.getFromRatio(),
mapList.getToRatio());
- AlignedCodonFrame cdsToProteinMapping = new AlignedCodonFrame();
- cdsToProteinMapping.addMap(cdsSeqDss, proteinProduct,
- cdsToProteinMap);
- /*
- * guard against duplicating the mapping if repeating this action
- */
- if (!mappings.contains(cdsToProteinMapping))
+ if (!dataset.getSequences().contains(cdsSeqDss))
{
- mappings.add(cdsToProteinMapping);
+ /*
+ * if this sequence is a newly created one, add it to the dataset
+ * and made a CDS to protein mapping (if sequence already exists,
+ * CDS-to-protein mapping _is_ the transcript-to-protein mapping)
+ */
+ dataset.addSequence(cdsSeqDss);
+ AlignedCodonFrame cdsToProteinMapping = new AlignedCodonFrame();
+ cdsToProteinMapping.addMap(cdsSeqDss, proteinProduct,
+ cdsToProteinMap);
+
+ /*
+ * guard against duplicating the mapping if repeating this action
+ */
+ if (!mappings.contains(cdsToProteinMapping))
+ {
+ mappings.add(cdsToProteinMapping);
+ }
}
propagateDBRefsToCDS(cdsSeqDss, dnaSeq.getDatasetSequence(),
return;
}
- MapList newMap = targetToFrom.traverse(fromLoci.getMap());
+ MapList newMap = targetToFrom.traverse(fromLoci.getMapping());
if (newMap != null)
{
static SequenceI makeCdsSequence(SequenceI seq, Mapping mapping,
AlignmentI dataset)
{
- char[] seqChars = seq.getSequence();
- List<int[]> fromRanges = mapping.getMap().getFromRanges();
- int cdsWidth = MappingUtils.getLength(fromRanges);
- char[] newSeqChars = new char[cdsWidth];
+ /*
+ * construct CDS sequence name as "CDS|" with 'from id' held in the mapping
+ * if set (e.g. EMBL protein_id), else sequence name appended
+ */
+ String mapFromId = mapping.getMappedFromId();
+ final String seqId = "CDS|"
+ + (mapFromId != null ? mapFromId : seq.getName());
+
+ SequenceI newSeq = null;
- int newPos = 0;
- for (int[] range : fromRanges)
+ final MapList maplist = mapping.getMap();
+ if (maplist.isContiguous() && maplist.isFromForwardStrand())
{
- if (range[0] <= range[1])
- {
- // forward strand mapping - just copy the range
- int length = range[1] - range[0] + 1;
- System.arraycopy(seqChars, range[0] - 1, newSeqChars, newPos,
- length);
- newPos += length;
- }
- else
+ /*
+ * just a subsequence, keep same dataset sequence
+ */
+ int start = maplist.getFromLowest();
+ int end = maplist.getFromHighest();
+ newSeq = seq.getSubSequence(start - 1, end);
+ newSeq.setName(seqId);
+ }
+ else
+ {
+ /*
+ * construct by splicing mapped from ranges
+ */
+ char[] seqChars = seq.getSequence();
+ List<int[]> fromRanges = maplist.getFromRanges();
+ int cdsWidth = MappingUtils.getLength(fromRanges);
+ char[] newSeqChars = new char[cdsWidth];
+
+ int newPos = 0;
+ for (int[] range : fromRanges)
{
- // reverse strand mapping - copy and complement one by one
- for (int i = range[0]; i >= range[1]; i--)
+ if (range[0] <= range[1])
{
- newSeqChars[newPos++] = Dna.getComplement(seqChars[i - 1]);
+ // forward strand mapping - just copy the range
+ int length = range[1] - range[0] + 1;
+ System.arraycopy(seqChars, range[0] - 1, newSeqChars, newPos,
+ length);
+ newPos += length;
+ }
+ else
+ {
+ // reverse strand mapping - copy and complement one by one
+ for (int i = range[0]; i >= range[1]; i--)
+ {
+ newSeqChars[newPos++] = Dna.getComplement(seqChars[i - 1]);
+ }
}
}
+
+ newSeq = new Sequence(seqId, newSeqChars, 1, newPos);
}
- /*
- * assign 'from id' held in the mapping if set (e.g. EMBL protein_id),
- * else generate a sequence name
- */
- String mapFromId = mapping.getMappedFromId();
- String seqId = "CDS|" + (mapFromId != null ? mapFromId : seq.getName());
- SequenceI newSeq = new Sequence(seqId, newSeqChars, 1, newPos);
if (dataset != null)
{
SequenceI[] matches = dataset.findSequenceMatch(newSeq.getName());
{
for (int ib = 0, nb = refs.size(); ib < nb; ib++)
{
- DBRefEntry dbr = refs.get(ib);
- MapList map;
+ DBRefEntry dbr = refs.get(ib);
+ MapList map;
if (dbr.hasMap() && (map = dbr.getMap().getMap()).isTripletMap())
{
// check if map is the CDS mapping
// and generate appropriate mappings
for (int ic = 0, nc = direct.size(); ic < nc; ic++)
{
- DBRefEntry cdsref = direct.get(ic);
- Mapping m = cdsref.getMap();
+ DBRefEntry cdsref = direct.get(ic);
+ Mapping m = cdsref.getMap();
// clone maplist and mapping
MapList cdsposmap = new MapList(
Arrays.asList(new int[][]
{ new int[] { cdsSeq.getStart(), cdsSeq.getEnd() } }),
m.getMap().getToRanges(), 3, 1);
- Mapping cdsmap = new Mapping(m.getTo(),m.getMap());
+ Mapping cdsmap = new Mapping(m.getTo(), m.getMap());
// create dbref
DBRefEntry newref = new DBRefEntry(cdsref.getSource(),
{
copyTo = copyTo.getDatasetSequence();
}
+ if (fromSeq == copyTo || fromSeq.getDatasetSequence() == copyTo)
+ {
+ return 0; // shared dataset sequence
+ }
/*
* get features, optionally restricted by an ontology term
}
} catch (NumberFormatException e)
{
- // SwingJS -- need to avoid these.
+ // leave as zero
}
/*
* phase > 0 on first codon means 5' incomplete - skip to the start
}
/**
- * Maps exon features from dna to protein, and computes variants in peptide
- * product generated by variants in dna, and adds them as sequence_variant
- * features on the protein sequence. Returns the number of variant features
- * added.
- *
- * @param dnaSeq
- * @param peptide
- * @param dnaToProtein
- */
- public static int computeProteinFeatures(SequenceI dnaSeq,
- SequenceI peptide, MapList dnaToProtein)
- {
- while (dnaSeq.getDatasetSequence() != null)
- {
- dnaSeq = dnaSeq.getDatasetSequence();
- }
- while (peptide.getDatasetSequence() != null)
- {
- peptide = peptide.getDatasetSequence();
- }
-
- transferFeatures(dnaSeq, peptide, dnaToProtein, SequenceOntologyI.EXON);
-
- /*
- * compute protein variants from dna variants and codon mappings;
- * NB - alternatively we could retrieve this using the REST service e.g.
- * http://rest.ensembl.org/overlap/translation
- * /ENSP00000288602?feature=transcript_variation;content-type=text/xml
- * which would be a bit slower but possibly more reliable
- */
-
- /*
- * build a map with codon variations for each potentially varying peptide
- */
- LinkedHashMap<Integer, List<DnaVariant>[]> variants = buildDnaVariantsMap(
- dnaSeq, dnaToProtein);
-
- /*
- * scan codon variations, compute peptide variants and add to peptide sequence
- */
- int count = 0;
- for (Entry<Integer, List<DnaVariant>[]> variant : variants.entrySet())
- {
- int peptidePos = variant.getKey();
- List<DnaVariant>[] codonVariants = variant.getValue();
- count += computePeptideVariants(peptide, peptidePos, codonVariants);
- }
-
- return count;
- }
-
- /**
- * Computes non-synonymous peptide variants from codon variants and adds them
- * as sequence_variant features on the protein sequence (one feature per
- * allele variant). Selected attributes (variant id, clinical significance)
- * are copied over to the new features.
- *
- * @param peptide
- * the protein sequence
- * @param peptidePos
- * the position to compute peptide variants for
- * @param codonVariants
- * a list of dna variants per codon position
- * @return the number of features added
- */
- static int computePeptideVariants(SequenceI peptide, int peptidePos,
- List<DnaVariant>[] codonVariants)
- {
- String residue = String.valueOf(peptide.getCharAt(peptidePos - 1));
- int count = 0;
- String base1 = codonVariants[0].get(0).base;
- String base2 = codonVariants[1].get(0).base;
- String base3 = codonVariants[2].get(0).base;
-
- /*
- * variants in first codon base
- */
- for (DnaVariant dnavar : codonVariants[0])
- {
- if (dnavar.variant != null)
- {
- String alleles = (String) dnavar.variant.getValue(Gff3Helper.ALLELES);
- if (alleles != null)
- {
- for (String base : alleles.split(","))
- {
- if (!base1.equalsIgnoreCase(base))
- {
- String codon = base.toUpperCase() + base2.toLowerCase()
- + base3.toLowerCase();
- String canonical = base1.toUpperCase() + base2.toLowerCase()
- + base3.toLowerCase();
- if (addPeptideVariant(peptide, peptidePos, residue, dnavar,
- codon, canonical))
- {
- count++;
- }
- }
- }
- }
- }
- }
-
- /*
- * variants in second codon base
- */
- for (DnaVariant var : codonVariants[1])
- {
- if (var.variant != null)
- {
- String alleles = (String) var.variant.getValue(Gff3Helper.ALLELES);
- if (alleles != null)
- {
- for (String base : alleles.split(","))
- {
- if (!base2.equalsIgnoreCase(base))
- {
- String codon = base1.toLowerCase() + base.toUpperCase()
- + base3.toLowerCase();
- String canonical = base1.toLowerCase() + base2.toUpperCase()
- + base3.toLowerCase();
- if (addPeptideVariant(peptide, peptidePos, residue, var,
- codon, canonical))
- {
- count++;
- }
- }
- }
- }
- }
- }
-
- /*
- * variants in third codon base
- */
- for (DnaVariant var : codonVariants[2])
- {
- if (var.variant != null)
- {
- String alleles = (String) var.variant.getValue(Gff3Helper.ALLELES);
- if (alleles != null)
- {
- for (String base : alleles.split(","))
- {
- if (!base3.equalsIgnoreCase(base))
- {
- String codon = base1.toLowerCase() + base2.toLowerCase()
- + base.toUpperCase();
- String canonical = base1.toLowerCase() + base2.toLowerCase()
- + base3.toUpperCase();
- if (addPeptideVariant(peptide, peptidePos, residue, var,
- codon, canonical))
- {
- count++;
- }
- }
- }
- }
- }
- }
-
- return count;
- }
-
- /**
- * Helper method that adds a peptide variant feature. ID and
- * clinical_significance attributes of the dna variant (if present) are copied
- * to the new feature.
- *
- * @param peptide
- * @param peptidePos
- * @param residue
- * @param var
- * @param codon
- * the variant codon e.g. aCg
- * @param canonical
- * the 'normal' codon e.g. aTg
- * @return true if a feature was added, else false
- */
- static boolean addPeptideVariant(SequenceI peptide, int peptidePos,
- String residue, DnaVariant var, String codon, String canonical)
- {
- /*
- * get peptide translation of codon e.g. GAT -> D
- * note that variants which are not single alleles,
- * e.g. multibase variants or HGMD_MUTATION etc
- * are currently ignored here
- */
- String trans = codon.contains("-") ? null
- : (codon.length() > CODON_LENGTH ? null
- : ResidueProperties.codonTranslate(codon));
- if (trans == null)
- {
- return false;
- }
- String desc = canonical + "/" + codon;
- String featureType = "";
- if (trans.equals(residue))
- {
- featureType = SequenceOntologyI.SYNONYMOUS_VARIANT;
- }
- else if (ResidueProperties.STOP.equals(trans))
- {
- featureType = SequenceOntologyI.STOP_GAINED;
- }
- else
- {
- String residue3Char = StringUtils
- .toSentenceCase(ResidueProperties.aa2Triplet.get(residue));
- String trans3Char = StringUtils
- .toSentenceCase(ResidueProperties.aa2Triplet.get(trans));
- desc = "p." + residue3Char + peptidePos + trans3Char;
- featureType = SequenceOntologyI.NONSYNONYMOUS_VARIANT;
- }
- SequenceFeature sf = new SequenceFeature(featureType, desc, peptidePos,
- peptidePos, var.getSource());
-
- StringBuilder attributes = new StringBuilder(32);
- String id = (String) var.variant.getValue(VARIANT_ID);
- if (id != null)
- {
- if (id.startsWith(SEQUENCE_VARIANT))
- {
- id = id.substring(SEQUENCE_VARIANT.length());
- }
- sf.setValue(VARIANT_ID, id);
- attributes.append(VARIANT_ID).append("=").append(id);
- // TODO handle other species variants JAL-2064
- StringBuilder link = new StringBuilder(32);
- try
- {
- link.append(desc).append(" ").append(id).append(
- "|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=")
- .append(URLEncoder.encode(id, "UTF-8"));
- sf.addLink(link.toString());
- } catch (UnsupportedEncodingException e)
- {
- // as if
- }
- }
- String clinSig = (String) var.variant.getValue(CLINICAL_SIGNIFICANCE);
- if (clinSig != null)
- {
- sf.setValue(CLINICAL_SIGNIFICANCE, clinSig);
- attributes.append(";").append(CLINICAL_SIGNIFICANCE).append("=")
- .append(clinSig);
- }
- peptide.addSequenceFeature(sf);
- if (attributes.length() > 0)
- {
- sf.setAttributes(attributes.toString());
- }
- return true;
- }
-
- /**
- * Builds a map whose key is position in the protein sequence, and value is a
- * list of the base and all variants for each corresponding codon position.
- * <p>
- * This depends on dna variants being held as a comma-separated list as
- * property "alleles" on variant features.
- *
- * @param dnaSeq
- * @param dnaToProtein
- * @return
- */
- @SuppressWarnings("unchecked")
- static LinkedHashMap<Integer, List<DnaVariant>[]> buildDnaVariantsMap(
- SequenceI dnaSeq, MapList dnaToProtein)
- {
- /*
- * map from peptide position to all variants of the codon which codes for it
- * LinkedHashMap ensures we keep the peptide features in sequence order
- */
- LinkedHashMap<Integer, List<DnaVariant>[]> variants = new LinkedHashMap<>();
-
- List<SequenceFeature> dnaFeatures = dnaSeq.getFeatures()
- .getFeaturesByOntology(SequenceOntologyI.SEQUENCE_VARIANT);
- if (dnaFeatures.isEmpty())
- {
- return variants;
- }
-
- int dnaStart = dnaSeq.getStart();
- int[] lastCodon = null;
- int lastPeptidePostion = 0;
-
- /*
- * build a map of codon variations for peptides
- */
- for (SequenceFeature sf : dnaFeatures)
- {
- int dnaCol = sf.getBegin();
- if (dnaCol != sf.getEnd())
- {
- // not handling multi-locus variant features
- continue;
- }
-
- /*
- * ignore variant if not a SNP
- */
- String alls = (String) sf.getValue(Gff3Helper.ALLELES);
- if (alls == null)
- {
- continue; // non-SNP VCF variant perhaps - can't process this
- }
-
- String[] alleles = alls.toUpperCase().split(",");
- boolean isSnp = true;
- for (String allele : alleles)
- {
- if (allele.trim().length() > 1)
- {
- isSnp = false;
- }
- }
- if (!isSnp)
- {
- continue;
- }
-
- int[] mapsTo = dnaToProtein.locateInTo(dnaCol, dnaCol);
- if (mapsTo == null)
- {
- // feature doesn't lie within coding region
- continue;
- }
- int peptidePosition = mapsTo[0];
- List<DnaVariant>[] codonVariants = variants.get(peptidePosition);
- if (codonVariants == null)
- {
- codonVariants = new ArrayList[CODON_LENGTH];
- codonVariants[0] = new ArrayList<>();
- codonVariants[1] = new ArrayList<>();
- codonVariants[2] = new ArrayList<>();
- variants.put(peptidePosition, codonVariants);
- }
-
- /*
- * get this peptide's codon positions e.g. [3, 4, 5] or [4, 7, 10]
- */
- int[] codon = peptidePosition == lastPeptidePostion ? lastCodon
- : MappingUtils.flattenRanges(dnaToProtein.locateInFrom(
- peptidePosition, peptidePosition));
- lastPeptidePostion = peptidePosition;
- lastCodon = codon;
-
- /*
- * save nucleotide (and any variant) for each codon position
- */
- for (int codonPos = 0; codonPos < CODON_LENGTH; codonPos++)
- {
- String nucleotide = String.valueOf(
- dnaSeq.getCharAt(codon[codonPos] - dnaStart)).toUpperCase();
- List<DnaVariant> codonVariant = codonVariants[codonPos];
- if (codon[codonPos] == dnaCol)
- {
- if (!codonVariant.isEmpty()
- && codonVariant.get(0).variant == null)
- {
- /*
- * already recorded base value, add this variant
- */
- codonVariant.get(0).variant = sf;
- }
- else
- {
- /*
- * add variant with base value
- */
- codonVariant.add(new DnaVariant(nucleotide, sf));
- }
- }
- else if (codonVariant.isEmpty())
- {
- /*
- * record (possibly non-varying) base value
- */
- codonVariant.add(new DnaVariant(nucleotide));
- }
- }
- }
- return variants;
- }
-
- /**
* Makes an alignment with a copy of the given sequences, adding in any
* non-redundant sequences which are mapped to by the cross-referenced
* sequences.
SequenceIdMatcher matcher = new SequenceIdMatcher(seqs);
if (xrefs != null)
{
- // BH 2019.01.25 streamlined this triply nested loop to remove all iterators
+ // BH 2019.01.25 recoded to remove iterators
for (int ix = 0, nx = xrefs.length; ix < nx; ix++)
{
{
for (int ir = 0, nir = dbrefs.size(); ir < nir; ir++)
{
- DBRefEntry dbref = dbrefs.get(ir);
- Mapping map = dbref.getMap();
- SequenceI mto;
+ DBRefEntry dbref = dbrefs.get(ir);
+ Mapping map = dbref.getMap();
+ SequenceI mto;
if (map == null || (mto = map.getTo()) == null
|| mto.isProtein() != isProtein)
{
* true; else returns false
*
* @param unaligned
- * - sequences to be aligned based on aligned
+ * - sequences to be aligned based on aligned
* @param aligned
- * - 'guide' alignment containing sequences derived from same dataset
- * as unaligned
+ * - 'guide' alignment containing sequences derived from same
+ * dataset as unaligned
* @return
*/
static boolean alignAsSameSequences(AlignmentI unaligned,
}
/*
- * first pass - check whether all sequences to be aligned share a dataset
- * sequence with an aligned sequence
+ * first pass - check whether all sequences to be aligned share a
+ * dataset sequence with an aligned sequence; also note the leftmost
+ * ungapped column from which to copy
*/
+ int leftmost = Integer.MAX_VALUE;
for (SequenceI seq : unaligned.getSequences())
{
- if (!alignedDatasets.containsKey(seq.getDatasetSequence()))
+ final SequenceI ds = seq.getDatasetSequence();
+ if (!alignedDatasets.containsKey(ds))
{
return false;
}
+ SequenceI alignedSeq = alignedDatasets.get(ds)
+ .get(0);
+ int startCol = alignedSeq.findIndex(seq.getStart()); // 1..
+ leftmost = Math.min(leftmost, startCol);
}
/*
* heuristic rule: pair off sequences in order for the case where
* more than one shares the same dataset sequence
*/
+ final char gapCharacter = aligned.getGapCharacter();
for (SequenceI seq : unaligned.getSequences())
{
List<SequenceI> alignedSequences = alignedDatasets
.get(seq.getDatasetSequence());
- // TODO: getSequenceAsString() will be deprecated in the future
- // TODO: need to leave to SequenceI implementor to update gaps
- seq.setSequence(alignedSequences.get(0).getSequenceAsString());
+ if (alignedSequences.isEmpty())
+ {
+ /*
+ * defensive check - shouldn't happen! (JAL-3536)
+ */
+ continue;
+ }
+ SequenceI alignedSeq = alignedSequences.get(0);
+
+ /*
+ * gap fill for leading (5') UTR if any
+ */
+ // TODO this copies intron columns - wrong!
+ int startCol = alignedSeq.findIndex(seq.getStart()); // 1..
+ int endCol = alignedSeq.findIndex(seq.getEnd());
+ char[] seqchars = new char[endCol - leftmost + 1];
+ Arrays.fill(seqchars, gapCharacter);
+ char[] toCopy = alignedSeq.getSequence(startCol - 1, endCol);
+ System.arraycopy(toCopy, 0, seqchars, startCol - leftmost,
+ toCopy.length);
+ seq.setSequence(String.valueOf(seqchars));
if (alignedSequences.size() > 0)
{
// pop off aligned sequences (except the last one)
}
}
+ /*
+ * finally remove gapped columns (e.g. introns)
+ */
+ new RemoveGapColCommand("", unaligned.getSequencesArray(), 0,
+ unaligned.getWidth() - 1, unaligned);
+
return true;
}
import jalview.util.DBRefUtils;
import jalview.util.MapList;
import jalview.ws.SequenceFetcher;
+import jalview.ws.seqfetcher.ASequenceFetcher;
import java.util.ArrayList;
import java.util.Iterator;
private void retrieveCrossRef(List<DBRefEntry> sourceRefs, SequenceI seq,
List<DBRefEntry> xrfs, boolean fromDna, AlignedCodonFrame cf)
{
+ ASequenceFetcher sftch = SequenceFetcher.getInstance();// Factory.getSequenceFetcher();
SequenceI[] retrieved = null;
SequenceI dss = seq.getDatasetSequence() == null ? seq
: seq.getDatasetSequence();
}
try
{
- retrieved = SequenceFetcher.getInstance()
- .getSequences(sourceRefs, !fromDna);
+ retrieved = sftch.getSequences(sourceRefs, !fromDna);
} catch (Exception e)
{
System.err.println(
@Override
public boolean equals(Object o)
{
- return equals((SequenceFeature) o, true);
+ return super.equals(o, true);
}
};
matched.addSequenceFeature(newFeature);
if (fromDna)
{
- AlignmentUtils.computeProteinFeatures(mapFrom, mapTo, mapping);
+ // AlignmentUtils.computeProteinFeatures(mapFrom, mapTo, mapping);
mappings.addMap(mapFrom, mapTo, mapping);
}
else
protected Map<SeqCigar, Set<String>> findFeatureTypesAtColumn(
SeqCigar[] seqs, int columnPosition)
{
- Map<SeqCigar, Set<String>> sfap = new HashMap<SeqCigar, Set<String>>();
+ Map<SeqCigar, Set<String>> sfap = new HashMap<>();
for (SeqCigar seq : seqs)
{
int spos = seq.findPosition(columnPosition);
/*
* position is not a gap
*/
- Set<String> types = new HashSet<String>();
+ Set<String> types = new HashSet<>();
List<SequenceFeature> sfs = fr.findFeaturesAtResidue(
- seq.getRefSeq(), spos);
+ seq.getRefSeq(), spos, spos);
for (SequenceFeature sf : sfs)
{
types.add(sf.getType());
import jalview.commands.CommandI;
import jalview.schemes.ColourSchemeI;
+import java.awt.Rectangle;
+
/**
* Interface implemented by gui implementations managing a Jalview Alignment
* View
* @return
*/
FeatureSettingsControllerI getFeatureSettingsUI();
+
+ /**
+ * displays the Feature Settigns control panel for the alignment view - if one
+ * exists it is closed and re-opened.
+ *
+ * @return the current feature settings controller
+ */
+ FeatureSettingsControllerI showFeatureSettingsUI();
+
+ /**
+ * record the last position of a feature settings dialog before it was closed
+ *
+ * @param bounds
+ */
+ void setFeatureSettingsGeometry(Rectangle bounds);
+
+ /**
+ *
+ * @return last position of feature settings for this alignment view GUI
+ */
+ Rectangle getFeatureSettingsGeometry();
}
* Sets the colour scheme for the background alignment (as distinct from
* sub-groups, which may have their own colour schemes). A null value is used
* for no residue colour (white).
- *
+ *
* @param cs
*/
void setGlobalColourScheme(ColourSchemeI cs);
*/
void setFollowHighlight(boolean b);
+ /**
+ * configure the feature renderer with predefined feature settings
+ *
+ * @param featureSettings
+ */
public void applyFeaturesStyle(FeatureSettingsModelI featureSettings);
/**
+ * Apply the given feature settings on top of existing feature settings.
+ */
+ public void mergeFeaturesStyle(FeatureSettingsModelI featureSettings);
+
+ /**
* check if current selection group is defined on the view, or is simply a
* temporary group.
*
*/
package jalview.api;
+import jalview.datamodel.MappedFeatures;
import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceI;
import jalview.datamodel.features.FeatureMatcherSetI;
List<SequenceFeature> findFeaturesAtColumn(SequenceI sequence, int column);
/**
- * Returns features at the specified residue position on the given sequence.
- * Non-positional features are not included.
+ * Returns features at the specified residue positions on the given sequence.
+ * Non-positional features are not included. Features are returned in render
+ * order of their feature type (last is on top). Within feature type, ordering
+ * is undefined.
*
* @param sequence
- * @param resNo
- * residue position (start..)
+ * @param fromResNo
+ * @param toResNo
* @return
*/
- List<SequenceFeature> findFeaturesAtResidue(SequenceI sequence, int resNo);
+ List<SequenceFeature> findFeaturesAtResidue(SequenceI sequence,
+ int fromResNo, int toResNo);
/**
* get current displayed types, in ordering of rendering (on top last)
* @return
*/
boolean isVisible(SequenceFeature feature);
+
+ /**
+ * Answers a bean containing a mapping, and a list of visible features in this
+ * alignment at a position (or range) which is mappable from the given sequence
+ * residue position in a mapped alignment. Features are returned in render order
+ * of feature type (on top last), with order within feature type undefined. If
+ * no features or mapping are found, answers null.
+ *
+ * @param sequence
+ * @param pos
+ * @return
+ */
+ MappedFeatures findComplementFeaturesAtResidue(SequenceI sequence, int pos);
}
*/
package jalview.api;
+import jalview.controller.FeatureSettingsControllerGuiI;
import jalview.datamodel.AlignmentI;
/**
*/
String getComplementTitle(Object af);
+ /**
+ * get the 'other' alignFrame in the SplitFrame
+ *
+ * @param alignFrame
+ * @return the complement alignFrame - or null if alignFrame wasn't held by this
+ * frame
+ */
+ AlignViewControllerGuiI getComplementAlignFrame(
+ AlignViewControllerGuiI alignFrame);
+
+ /**
+ * add the given UI to the splitframe's feature settings UI holder
+ *
+ * @param featureSettings
+ * @return
+ */
+ void addFeatureSettingsUI(
+ FeatureSettingsControllerGuiI featureSettings);
+
+ /**
+ * Request to close all feature settings originating from a particular panel.
+ *
+ * @param featureSettings
+ * @param closeContainingFrame
+ * - if false then the tab containing the feature
+ * settings will be 'reset' ready for a new
+ * feature settings
+ */
+ void closeFeatureSettings(FeatureSettingsControllerI featureSettings,
+ boolean closeContainingFrame);
+
+ /**
+ *
+ * @return true if a feature settings panel is currently open
+ */
+ boolean isFeatureSettingsOpen();
+
}
public interface ViewStyleI
{
+ void setShowComplementFeatures(boolean b);
+
+ boolean isShowComplementFeatures();
+
+ void setShowComplementFeaturesOnTop(boolean b);
+
+ boolean isShowComplementFeaturesOnTop();
void setColourAppliesToAllGroups(boolean b);
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.Panel;
+import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
+import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Arrays;
import org.jmol.viewer.Viewer;
-public class AlignFrame extends EmbmenuFrame implements AlignFrameI,
- ActionListener,
- ItemListener, KeyListener, AlignViewControllerGuiI
+public class AlignFrame extends EmbmenuFrame implements ActionListener,
+ ItemListener, KeyListener, AlignViewControllerGuiI, AlignFrameI
{
public AlignViewControllerI avc;
}
else if (source == featureSettings)
{
- new FeatureSettings(alignPanel);
+ showFeatureSettingsUI();
}
else if (source == alProperties)
{
{
features = formatter.printJalviewFormat(
viewport.getAlignment().getSequencesArray(),
- alignPanel.getFeatureRenderer(), true);
+ alignPanel.getFeatureRenderer(), true, false);
}
else
{
features = formatter.printGffFormat(viewport.getAlignment()
- .getSequencesArray(), alignPanel.getFeatureRenderer(), true);
+ .getSequencesArray(), alignPanel.getFeatureRenderer(), true,
+ false);
}
if (displayTextbox)
try
{
new URL(url);
- url = URLEncoder.encode(url);
+ url = URLEncoder.encode(url, "UTF-8");
}
/*
* When we finally deprecate 1.1 compatibility, we can start to use
{
url = viewport.applet.getCodeBase() + url;
}
+ catch (UnsupportedEncodingException ex)
+ {
+ System.err.println(
+ "WARNING = IMPLEMENTATION ERROR - UNSUPPORTED ENCODING EXCEPTION FOR "
+ + url);
+ ex.printStackTrace();
+ }
return url;
}
return alignPanel.av.featureSettings;
}
+ @Override
+ public FeatureSettingsControllerI showFeatureSettingsUI()
+ {
+ return new FeatureSettings(alignPanel);
+ }
+
+ private Rectangle fs_bounds = null;
+
+ @Override
+ public void setFeatureSettingsGeometry(Rectangle bounds)
+ {
+ fs_bounds = bounds;
+ }
+
+ @Override
+ public Rectangle getFeatureSettingsGeometry()
+ {
+ return fs_bounds;
+ }
+
}
{
try
{
- widthScale = new Float(param).floatValue();
+ widthScale = Float.valueOf(param).floatValue();
} catch (Exception e)
{
}
{
try
{
- heightScale = new Float(param).floatValue();
+ heightScale = Float.valueOf(param).floatValue();
} catch (Exception e)
{
}
}
/**
- * Applies the supplied feature settings descriptor to currently known
- * features. This supports an 'initial configuration' of feature colouring
- * based on a preset or user favourite. This may then be modified in the usual
- * way using the Feature Settings dialogue.
+ * Applies the supplied feature settings descriptor to currently known features.
+ * This supports an 'initial configuration' of feature colouring based on a
+ * preset or user favourite. This may then be modified in the usual way using
+ * the Feature Settings dialogue. NOT IMPLEMENTED FOR APPLET
*
* @param featureSettings
*/
// TODO implement for applet
}
+ /**
+ * Merges the supplied feature settings descriptor with existing feature styles.
+ * This supports an 'initial configuration' of feature colouring based on a
+ * preset or user favourite. This may then be modified in the usual way using
+ * the Feature Settings dialogue. NOT IMPLEMENTED FOR APPLET
+ *
+ * @param featureSettings
+ */
+ @Override
+ public void mergeFeaturesStyle(FeatureSettingsModelI featureSettings)
+ {
+ // TODO Auto-generated method stub
+
+ }
}
// For now, ignore the mouseWheel font resizing on Macs
// As the Button2_mask always seems to be true
-
if (Platform.isWinMiddleButton(evt))
{
mouseWheelPressed = true;
}
@Override
- public void highlightSequence(SearchResultsI results)
+ public String highlightSequence(SearchResultsI results)
{
if (av.isFollowHighlight())
{
}
setStatusMessage(results);
seqCanvas.highlightSearchResults(results);
-
+ return null;
}
@Override
{
if (av.getAlignment().isNucleotide())
{
- String base = ResidueProperties.nucleotideName
- .get(String.valueOf(ch)); // BH 2020.04.07 just Eclipse warning
+ String base = ResidueProperties.nucleotideName.get(ch);
text.append(" Nucleotide: ").append(base == null ? ch : base);
}
else
}
// DETECT RIGHT MOUSE BUTTON IN AWT
- if ((evt.getModifiers()
- & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
+ if ((evt.getModifiersEx()
+ & InputEvent.BUTTON3_DOWN_MASK) == InputEvent.BUTTON3_DOWN_MASK)
{
List<SequenceFeature> allFeatures = findFeaturesAtColumn(sequence,
sequence.findPosition(column + 1));
System.out.println("No files to open!");
System.exit(1);
}
- boolean haveImport = checkStartVamas(aparser);
+ boolean haveImport = false;// checkStartVamas(aparser);
// Finally, deal with the remaining input data.
long progress = -1;
if (file == null && isJavaAppletTag)
System.out.println("CMD [nocalculation] executed successfully!");
}
- AlignFrame af = new FileLoader(!headless).loadFileWaitTillLoaded(file,
+ AlignFrame af = new FileLoader(!headless).LoadFileWaitTillLoaded(file,
protocol, format);
if (af == null)
{
// TODO ?
}
AlignFrame af2 = new FileLoader(!headless)
- .loadFileWaitTillLoaded(file2, protocol, format);
+ .LoadFileWaitTillLoaded(file2, protocol, format);
if (af2 == null)
{
System.out.println("error");
}
startUpAlframe = new FileLoader(!headless)
- .loadFileWaitTillLoaded(file, protocol, format);
+ .LoadFileWaitTillLoaded(file, protocol, format);
// extract groovy arguments before anything else.
}
return null;
}
- private boolean checkStartVamas(ArgsParser aparser)
- {
- String vamsasImport = aparser.getValue(ArgsParser.VDOC);
- String vamsasSession = aparser.getValue(ArgsParser.VSESS);
- if (vamsasImport == null && vamsasSession == null)
- {
- return false;
- }
- if (desktop == null || headless)
- {
- System.out.println(
- "Headless vamsas sessions not yet supported. Sorry.");
- System.exit(1);
- }
- boolean haveImport = (vamsasImport != null);
- if (haveImport)
- {
- // if we have a file, start a new session and import it.
- boolean inSession = false;
- try
- {
- DataSourceType viprotocol = AppletFormatAdapter
- .checkProtocol(vamsasImport);
- if (viprotocol == DataSourceType.FILE)
- {
- inSession = desktop.vamsasImport(new File(vamsasImport));
- }
- else if (viprotocol == DataSourceType.URL)
- {
- inSession = desktop.vamsasImport(new URL(vamsasImport));
- }
-
- } catch (Exception e)
- {
- System.err.println("Exeption when importing " + vamsasImport
- + " as a vamsas document.");
- e.printStackTrace();
- }
- if (!inSession)
- {
- System.err.println("Failed to import " + vamsasImport
- + " as a vamsas document.");
- }
- else
- {
- System.out.println("Imported Successfully into new session "
- + desktop.getVamsasApplication().getCurrentSession());
- }
- }
- if (vamsasSession != null)
- {
- if (vamsasImport != null)
- {
- // close the newly imported session and import the Jalview specific
- // remnants into the new session later on.
- desktop.vamsasStop_actionPerformed(null);
- }
- // now join the new session
- try
- {
- if (desktop.joinVamsasSession(vamsasSession))
- {
- System.out.println(
- "Successfully joined vamsas session " + vamsasSession);
- }
- else
- {
- System.err.println("WARNING: Failed to join vamsas session "
- + vamsasSession);
- }
- } catch (Exception e)
- {
- System.err.println(
- "ERROR: Failed to join vamsas session " + vamsasSession);
- e.printStackTrace();
- }
- if (vamsasImport != null)
- {
- // the Jalview specific remnants can now be imported into the new
- // session at the user's leisure.
- Cache.log.info(
- "Skipping Push for import of data into existing vamsas session.");
- // TODO:
- // enable
- // this
- // when
- // debugged
- // desktop.getVamsasApplication().push_update();
- }
- }
- return haveImport;
- }
-
/**
* Writes an output file for each format (if any) specified in the
* command-line arguments. Supported formats are currently
}
/**
- * Get the SwingJS applet ID and combine that with the frameType
- *
- * @param frameType
- * "alignment", "desktop", etc., or null
- * @return
- */
- public static String getAppID(String frameType)
- {
- String id = Cache.getProperty("Info.j2sAppletID");
- if (id == null)
- {
- id = "jalview";
- }
- return id + (frameType == null ? "" : "-" + frameType);
- }
-
- /**
* Handle all JalviewLite applet parameters
*
* @param aparser
{
alf = getCurrentAlignFrame();
}
- return appLoader.getFeaturesFrom(alf, format);
+ return appLoader.getFeaturesFrom(alf, format, true, false);
}
@Override
}
}
+ /**
+ * Get the SwingJS applet ID and combine that with the frameType
+ *
+ * @param frameType
+ * "alignment", "desktop", etc., or null
+ * @return
+ */
+ public static String getAppID(String frameType)
+ {
+ String id = Cache.getProperty("Info.j2sAppletID");
+ if (id == null)
+ {
+ id = "jalview";
+ }
+ return id + (frameType == null ? "" : "-" + frameType);
+ }
+
}
\ No newline at end of file
return true;
}
- public String getFeaturesFrom(AlignFrameI alf, String format)
+ public String getFeaturesFrom(AlignFrameI alf, String format,
+ boolean includeNonpositionsFeatures, boolean includeComplement)
{
AlignFrame f = ((AlignFrame) alf);
{
features = formatter.printJalviewFormat(
f.getViewport().getAlignment().getSequencesArray(),
- f.alignPanel.getFeatureRenderer(), true);
+ f.alignPanel.getFeatureRenderer(),
+ includeNonpositionsFeatures, includeComplement);
}
else
{
features = formatter.printGffFormat(
f.getViewport().getAlignment().getSequencesArray(),
- f.alignPanel.getFeatureRenderer(), true);
+ f.alignPanel.getFeatureRenderer(),
+ includeNonpositionsFeatures, includeComplement);
}
if (features == null)
// JBPNote this routine could also mark rows, not just columns.
// need a decent query structure to allow all types of feature searches
BitSet bs = new BitSet();
- SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null
- || extendCurrent) ? viewport.getAlignment()
- : viewport.getSelectionGroup();
+ boolean searchSelection = viewport.getSelectionGroup() != null
+ && !extendCurrent;
+ SequenceCollectionI sqcol = searchSelection ? viewport
+ .getSelectionGroup() : viewport.getAlignment();
int nseq = findColumnsWithFeature(featureType, sqcol, bs);
}
else
{
- avcg.setStatus(MessageManager
- .formatMessage("label.no_feature_of_type_found", new String[]
- { featureType }));
+ String key = searchSelection ? "label.no_feature_found_selection"
+ : "label.no_feature_of_type_found";
+ avcg.setStatus(MessageManager.formatMessage(key,
+ new String[] { featureType }));
if (!extendCurrent)
{
cs.clear();
@Override
public void sortAlignmentByFeatureDensity(List<String> typ)
{
- sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
+ String methodText = MessageManager.getString("label.sort_by_density");
+ sortByFeatures(typ, methodText, AlignmentSorter.FEATURE_DENSITY);
}
- protected void sortBy(List<String> typ, String methodText,
+ /**
+ * Sorts the alignment (or current selection) by either average score or density
+ * of the specified feature types, and adds to the command history. If
+ * {@code types} is null, all visible feature types are used for the sort. If no
+ * feature types apply, does nothing.
+ *
+ * @param types
+ * @param methodText
+ * - text shown in Undo/Redo command
+ * @param method
+ * - passed to
+ * jalview.analysis.AlignmentSorter.sortByFeatures()
+ */
+ protected void sortByFeatures(List<String> types, String methodText,
final String method)
{
FeatureRenderer fr = alignPanel.getFeatureRenderer();
- if (typ == null && fr != null)
+ if (types == null && fr != null)
+ {
+ types = fr.getDisplayedFeatureTypes();
+ }
+ if (types.isEmpty())
{
- typ = fr.getDisplayedFeatureTypes();
+ return; // nothing to do
}
List<String> gps = null;
if (fr != null)
stop = al.getWidth();
}
SequenceI[] oldOrder = al.getSequencesArray();
- AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
+ AlignmentSorter.sortByFeature(types, gps, start, stop, al, method);
avcg.addHistoryItem(new OrderCommand(methodText, oldOrder,
viewport.getAlignment()));
alignPanel.paintAlignment(true, false);
@Override
public void sortAlignmentByFeatureScore(List<String> typ)
{
- sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);
+ String methodText = MessageManager.getString("label.sort_by_score");
+ sortByFeatures(typ, methodText, AlignmentSorter.FEATURE_SCORE);
}
@Override
*/
package jalview.controller;
+import jalview.api.AlignViewControllerGuiI;
+
public interface FeatureSettingsControllerGuiI
{
+ AlignViewControllerGuiI getAlignframe();
+
+ void featureSettings_isClosed();
+
+ /**
+ * undo any changes made to feature settings whilst the dialog has been visible,
+ * since the last 'apply'
+ */
+ void revert();
+
}
: new String(entry.getVersion())),
(entry.getAccessionId() == null ? ""
: new String(entry.getAccessionId())),
- (entry.getMap() == null ? null : new Mapping(entry.getMap())));
+ (entry.getMap() == null ? null
+ : new Mapping(entry.getMap())));
}
@Override
*
* @return
*/
- MapList getMap();
+ MapList getMapping();
}
--- /dev/null
+package jalview.datamodel;
+
+import jalview.util.MapList;
+
+/**
+ * A specialisation of DBRefEntry used to hold the chromosomal coordinates for a
+ * (typically gene) sequence
+ * <ul>
+ * <li>field <code>source</code> is used to hold a species id e.g. human</li>
+ * <li>field <code>version</code> is used to hold assembly id e.g GRCh38</li>
+ * <li>field <code>accession</code> is used to hold the chromosome id</li>
+ * <li>field <code>map</code> is used to hold the mapping from sequence to
+ * chromosome coordinates</li>
+ * </ul>
+ *
+ * @author gmcarstairs
+ *
+ */
+public class GeneLocus extends DBRefEntry implements GeneLociI
+{
+ /**
+ * Constructor adapts species, assembly, chromosome to DBRefEntry source,
+ * version, accession, respectively, and saves the mapping of sequence to
+ * chromosomal coordinates
+ *
+ * @param speciesId
+ * @param assemblyId
+ * @param chromosomeId
+ * @param mapping
+ */
+ public GeneLocus(String speciesId, String assemblyId, String chromosomeId,
+ Mapping mapping)
+ {
+ super(speciesId, assemblyId, chromosomeId, mapping);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param speciesId
+ * @param assemblyId
+ * @param chromosomeId
+ */
+ public GeneLocus(String speciesId, String assemblyId, String chromosomeId)
+ {
+ this(speciesId, assemblyId, chromosomeId, null);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ return o instanceof GeneLocus && super.equals(o);
+ }
+
+ @Override
+ public MapList getMapping()
+ {
+ return map == null ? null : map.getMap();
+ }
+
+ /**
+ * Answers the species identifier e.g. "human", stored as field <code>source</code> of
+ * DBRefEntry
+ */
+ @Override
+ public String getSpeciesId()
+ {
+ return getSource();
+ }
+
+ /**
+ * Answers the genome assembly id e.g. "GRCh38", stored as field
+ * <code>version</code> of DBRefEntry
+ */
+ @Override
+ public String getAssemblyId()
+ {
+ return getVersion();
+ }
+
+ /**
+ * Answers the chromosome identifier e.g. "X", stored as field
+ * <code>accession</code> of DBRefEntry
+ */
+ @Override
+ public String getChromosomeId()
+ {
+ return getAccessionId();
+ }
+
+}
--- /dev/null
+package jalview.datamodel;
+
+import jalview.io.gff.Gff3Helper;
+import jalview.schemes.ResidueProperties;
+import jalview.util.MappingUtils;
+import jalview.util.StringUtils;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A data bean to hold a list of mapped sequence features (e.g. CDS features
+ * mapped from protein), and the mapping between the sequences. It also provides
+ * a method to derive peptide variants from codon variants.
+ *
+ * @author gmcarstairs
+ */
+public class MappedFeatures
+{
+ private static final String HGV_SP = "HGVSp";
+
+ private static final String CSQ = "CSQ";
+
+ /*
+ * the mapping from one sequence to another
+ */
+ public final Mapping mapping;
+
+ /**
+ * the sequence mapped from
+ */
+ public final SequenceI fromSeq;
+
+ /*
+ * features on the sequence mapped to that overlap the mapped positions
+ */
+ public final List<SequenceFeature> features;
+
+ /*
+ * the residue position in the sequence mapped to
+ */
+ private final int toPosition;
+
+ /*
+ * the residue at toPosition
+ */
+ private final char toResidue;
+
+ /*
+ * if the mapping is 3:1 or 1:3 (peptide to CDS), this holds the
+ * mapped positions i.e. codon base positions in CDS; to
+ * support calculation of peptide variants from alleles
+ */
+ private final int[] codonPos;
+
+ private final char[] baseCodon;
+
+ /**
+ * Constructor
+ *
+ * @param theMapping
+ * @param from
+ * the sequence mapped from (e.g. CDS)
+ * @param pos
+ * the residue position in the sequence mapped to
+ * @param res
+ * the residue character at position pos
+ * @param theFeatures
+ * list of mapped features found in the 'from' sequence at
+ * the mapped position(s)
+ */
+ public MappedFeatures(Mapping theMapping, SequenceI from, int pos,
+ char res, List<SequenceFeature> theFeatures)
+ {
+ mapping = theMapping;
+ fromSeq = from;
+ toPosition = pos;
+ toResidue = res;
+ features = theFeatures;
+
+ /*
+ * determine codon positions and canonical codon
+ * for a peptide-to-CDS mapping
+ */
+ int[] codonIntervals = mapping.getMap().locateInFrom(toPosition, toPosition);
+ int[] codonPositions = codonIntervals == null ? null
+ : MappingUtils.flattenRanges(codonIntervals);
+ if (codonPositions != null && codonPositions.length == 3)
+ {
+ codonPos = codonPositions;
+ baseCodon = new char[3];
+ int cdsStart = fromSeq.getStart();
+ baseCodon[0] = Character
+ .toUpperCase(fromSeq.getCharAt(codonPos[0] - cdsStart));
+ baseCodon[1] = Character
+ .toUpperCase(fromSeq.getCharAt(codonPos[1] - cdsStart));
+ baseCodon[2] = Character
+ .toUpperCase(fromSeq.getCharAt(codonPos[2] - cdsStart));
+ }
+ else
+ {
+ codonPos = null;
+ baseCodon = null;
+ }
+ }
+
+ /**
+ * Computes and returns comma-delimited HGVS notation peptide variants derived
+ * from codon allele variants. If no variants are found, answers an empty
+ * string.
+ *
+ * @param sf
+ * a sequence feature (which must be one of those held in this
+ * object)
+ * @return
+ */
+ public String findProteinVariants(SequenceFeature sf)
+ {
+ if (!features.contains(sf) || baseCodon == null)
+ {
+ return "";
+ }
+
+ /*
+ * VCF data may already contain the protein consequence
+ */
+ String hgvsp = sf.getValueAsString(CSQ, HGV_SP);
+ if (hgvsp != null)
+ {
+ int colonPos = hgvsp.lastIndexOf(':');
+ if (colonPos >= 0)
+ {
+ String var = hgvsp.substring(colonPos + 1);
+ if (var.contains("p.")) // sanity check
+ {
+ return var;
+ }
+ }
+ }
+
+ /*
+ * otherwise, compute codon and peptide variant
+ */
+ int cdsPos = sf.getBegin();
+ if (cdsPos != sf.getEnd())
+ {
+ // not handling multi-locus variant features
+ return "";
+ }
+ if (cdsPos != codonPos[0] && cdsPos != codonPos[1]
+ && cdsPos != codonPos[2])
+ {
+ // e.g. feature on intron within spliced codon!
+ return "";
+ }
+
+ String alls = (String) sf.getValue(Gff3Helper.ALLELES);
+ if (alls == null)
+ {
+ return "";
+ }
+
+ String from3 = StringUtils.toSentenceCase(
+ ResidueProperties.aa2Triplet.get(String.valueOf(toResidue)));
+
+ /*
+ * make a peptide variant for each SNP allele
+ * e.g. C,G,T gives variants G and T for base C
+ */
+ Set<String> variantPeptides = new HashSet<>();
+ String[] alleles = alls.toUpperCase().split(",");
+ StringBuilder vars = new StringBuilder();
+
+ for (String allele : alleles)
+ {
+ allele = allele.trim().toUpperCase();
+ if (allele.length() > 1 || "-".equals(allele))
+ {
+ continue; // multi-locus variant
+ }
+ char[] variantCodon = new char[3];
+ variantCodon[0] = baseCodon[0];
+ variantCodon[1] = baseCodon[1];
+ variantCodon[2] = baseCodon[2];
+
+ /*
+ * poke variant base into canonical codon;
+ * ignore first 'allele' (canonical base)
+ */
+ final int i = cdsPos == codonPos[0] ? 0
+ : (cdsPos == codonPos[1] ? 1 : 2);
+ variantCodon[i] = allele.toUpperCase().charAt(0);
+ if (variantCodon[i] == baseCodon[i])
+ {
+ continue;
+ }
+ String codon = new String(variantCodon);
+ String peptide = ResidueProperties.codonTranslate(codon);
+ boolean synonymous = toResidue == peptide.charAt(0);
+ StringBuilder var = new StringBuilder();
+ if (synonymous)
+ {
+ /*
+ * synonymous variant notation e.g. c.1062C>A(p.=)
+ */
+ var.append("c.").append(String.valueOf(cdsPos))
+ .append(String.valueOf(baseCodon[i])).append(">")
+ .append(String.valueOf(variantCodon[i]))
+ .append("(p.=)");
+ }
+ else
+ {
+ /*
+ * missense variant notation e.g. p.Arg355Met
+ */
+ String to3 = ResidueProperties.STOP.equals(peptide) ? "Ter"
+ : StringUtils.toSentenceCase(
+ ResidueProperties.aa2Triplet.get(peptide));
+ var.append("p.").append(from3).append(String.valueOf(toPosition))
+ .append(to3);
+ }
+ if (!variantPeptides.contains(peptide)) // duplicate consequence
+ {
+ variantPeptides.add(peptide);
+ if (vars.length() > 0)
+ {
+ vars.append(",");
+ }
+ vars.append(var);
+ }
+ }
+
+ return vars.toString();
+ }
+}
}
@Override
- public MapList getMap()
+ public MapList getMapping()
{
return ref.getMap().getMap();
}
import jalview.util.StringUtils;
import java.util.Comparator;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
private static final String STATUS = "status";
- private static final String STRAND = "STRAND";
+ public static final String STRAND = "STRAND";
- // private key for Phase designed not to conflict with real GFF data
- private static final String PHASE = "!Phase";
+ // key for Phase designed not to conflict with real GFF data
+ public static final String PHASE = "!Phase";
// private key for ENA location designed not to conflict with real GFF data
private static final String LOCATION = "!Location";
private static final String ROW_DATA = "<tr><td>%s</td><td>%s</td><td>%s</td></tr>";
/*
- * ATTRIBUTES is reserved for the GFF 'column 9' data, formatted as
- * name1=value1;name2=value2,value3;...etc
- */
- private static final String ATTRIBUTES = "ATTRIBUTES";
-
- /*
* type, begin, end, featureGroup, score and contactFeature are final
* to ensure that the integrity of SequenceFeatures data store
* can't be broken by direct update of these fields
if (sf.otherDetails != null)
{
- otherDetails = new HashMap<>();
- for (Entry<String, Object> entry : sf.otherDetails.entrySet())
- {
- otherDetails.put(entry.getKey(), entry.getValue());
- }
+ otherDetails = new LinkedHashMap<>();
+ otherDetails.putAll(sf.otherDetails);
}
if (sf.links != null && sf.links.size() > 0)
{
links = new Vector<>();
- for (int i = 0, iSize = sf.links.size(); i < iSize; i++)
- {
- links.addElement(sf.links.elementAt(i));
- }
+ links.addAll(sf.links);
}
}
@Override
public boolean equals(Object o)
{
- return (o instanceof SequenceFeature
- && equals((SequenceFeature) o, false));
+ return equals(o, false);
}
/**
* @param ignoreParent
* @return
*/
- public boolean equals(SequenceFeature sf, boolean ignoreParent)
+ public boolean equals(Object o, boolean ignoreParent)
{
- return (begin == sf.begin && end == sf.end
- && getStrand() == sf.getStrand()
- && (Float.isNaN(score) ? Float.isNaN(sf.score)
- : score == sf.score)
- && (type + description + featureGroup + getPhase())
- .equals(sf.type + sf.description + sf.featureGroup
- + sf.getPhase())
- && equalAttribute(getValue("ID"), sf.getValue("ID"))
- && equalAttribute(getValue("Name"), sf.getValue("Name"))
- && (ignoreParent || equalAttribute(getValue("Parent"),
- sf.getValue("Parent"))));
+ if (o == null || !(o instanceof SequenceFeature))
+ {
+ return false;
+ }
+
+ SequenceFeature sf = (SequenceFeature) o;
+ boolean sameScore = Float.isNaN(score) ? Float.isNaN(sf.score)
+ : score == sf.score;
+ if (begin != sf.begin || end != sf.end || !sameScore)
+ {
+ return false;
+ }
+
+ if (getStrand() != sf.getStrand())
+ {
+ return false;
+ }
+
+ if (!(type + description + featureGroup + getPhase()).equals(
+ sf.type + sf.description + sf.featureGroup + sf.getPhase()))
+ {
+ return false;
+ }
+ if (!equalAttribute(getValue("ID"), sf.getValue("ID")))
+ {
+ return false;
+ }
+ if (!equalAttribute(getValue("Name"), sf.getValue("Name")))
+ {
+ return false;
+ }
+ if (!ignoreParent)
+ {
+ if (!equalAttribute(getValue("Parent"), sf.getValue("Parent")))
+ {
+ return false;
+ }
+ }
+ return true;
}
/**
return featureGroup;
}
+ /**
+ * Adds a hyperlink for the feature. This should have the format label|url.
+ *
+ * @param labelLink
+ */
public void addLink(String labelLink)
{
if (links == null)
{
if (otherDetails == null)
{
- otherDetails = new HashMap<>();
+ /*
+ * LinkedHashMap preserves insertion order of attributes
+ */
+ otherDetails = new LinkedHashMap<>();
}
otherDetails.put(key, value);
return (String) getValue(STATUS);
}
- public void setAttributes(String attr)
- {
- setValue(ATTRIBUTES, attr);
- }
-
- public String getAttributes()
- {
- return (String) getValue(ATTRIBUTES);
- }
-
/**
* Return 1 for forward strand ('+' in GFF), -1 for reverse strand ('-' in
* GFF), and 0 for unknown or not (validly) specified
/**
* Answers an html-formatted report of feature details
*
+ * @param seqName
+ *
* @return
*/
- public String getDetailsReport()
+ public String getDetailsReport(String seqName)
{
FeatureSourceI metadata = FeatureSources.getInstance()
.getSource(source);
StringBuilder sb = new StringBuilder(128);
sb.append("<br>");
sb.append("<table>");
+ sb.append(String.format(ROW_DATA, "Location", seqName,
+ begin == end ? begin
+ : begin + (isContactFeature() ? ":" : "-") + end));
sb.append(String.format(ROW_DATA, "Type", type, ""));
- sb.append(String.format(ROW_DATA, "Start/end", begin == end ? begin
- : begin + (isContactFeature() ? ":" : "-") + end, ""));
String desc = StringUtils.stripHtmlTags(description);
sb.append(String.format(ROW_DATA, "Description", desc, ""));
if (!Float.isNaN(score) && score != 0f)
for (Entry<String, Object> entry : ordered.entrySet())
{
String key = entry.getKey();
- if (ATTRIBUTES.equals(key))
- {
- continue; // to avoid double reporting
- }
Object value = entry.getValue();
if (value instanceof Map<?, ?>)
{
source = theSource;
}
-
-
}
class SFSortByEnd implements Comparator<SequenceFeature>
import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceI;
import jalview.io.gff.SequenceOntologyI;
-import jalview.util.Platform;
import java.util.ArrayList;
import java.util.List;
* or ENSMUST or similar for other species
* or CCDSnnnnn.nn with at least 3 digits
*/
- private static Regex ACCESSION_REGEX;
+ private static final Regex ACCESSION_REGEX = new Regex(
+ "(ENS([A-Z]{3}|)[TG][0-9]{11}$)" + "|" + "(CCDS[0-9.]{3,}$)");
+
/*
* fetch exon features on genomic sequence (to identify the cdna regions)
* and cds and variation features (to retain)
@Override
public Regex getAccessionValidator()
{
- if (ACCESSION_REGEX == null)
- {
- /*
- * accepts ENSG/T/E/P with 11 digits
- * or ENSMUSP or similar for other species
- * or CCDSnnnnn.nn with at least 3 digits
- */
- ACCESSION_REGEX = Platform.newRegex(
- "(ENS([A-Z]{3}|)[TG][0-9]{11}$)" + "|" + "(CCDS[0-9.]{3,}$)", null);
- }
return ACCESSION_REGEX;
}
import jalview.datamodel.SequenceI;
import jalview.io.gff.SequenceOntologyI;
import jalview.util.JSONUtils;
+import jalview.util.Platform;
+import java.io.BufferedReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
List<String> queries = new ArrayList<>();
queries.add(query);
SequenceI seq = parseFeaturesJson(queries);
+ if (seq == null)
+ return null;
return new Alignment(new SequenceI[] { seq });
+
}
/**
int end = Integer.parseInt(obj.get("end").toString());
String source = obj.get("source").toString();
String strand = obj.get("strand").toString();
+ Object phase = obj.get("phase");
String alleles = JSONUtils
.arrayToStringList((List<Object>) obj.get("alleles"));
String clinSig = JSONUtils
SequenceFeature sf = new SequenceFeature(type, desc, start, end,
source);
sf.setStrand("1".equals(strand) ? "+" : "-");
+ if (phase != null)
+ {
+ sf.setPhase(phase.toString());
+ }
setFeatureAttribute(sf, obj, "id");
setFeatureAttribute(sf, obj, "Parent");
setFeatureAttribute(sf, obj, "consequence_type");
import jalview.api.FeatureColourI;
import jalview.api.FeatureSettingsModelI;
import jalview.datamodel.AlignmentI;
-import jalview.datamodel.DBRefEntry;
import jalview.datamodel.GeneLociI;
import jalview.datamodel.Sequence;
import jalview.datamodel.SequenceFeature;
EnsemblFeatureType.exon, EnsemblFeatureType.cds,
EnsemblFeatureType.variation };
+ private static final String CHROMOSOME = "chromosome";
+
/**
* Default constructor (to use rest.ensembl.org)
*/
if (geneLoci != null)
{
seq.setGeneLoci(geneLoci.getSpeciesId(), geneLoci.getAssemblyId(),
- geneLoci.getChromosomeId(), geneLoci.getMap());
+ geneLoci.getChromosomeId(), geneLoci.getMapping());
}
else
{
return false;
}
String[] tokens = description.split(":");
- if (tokens.length == 6 && tokens[0].startsWith(DBRefEntry.CHROMOSOME))
+ if (tokens.length == 6 && tokens[0].startsWith(CHROMOSOME))
{
String ref = tokens[1];
String chrom = tokens[2];
return;
}
- MapList geneMapping = loci.getMap();
+ MapList geneMapping = loci.getMapping();
List<int[]> exons = mapping.getFromRanges();
List<int[]> transcriptLoci = new ArrayList<>();
import jalview.datamodel.AlignmentI;
import jalview.datamodel.DBRefSource;
+import jalview.util.JSONUtils;
+import java.io.BufferedReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
public class EnsemblInfo extends EnsemblRestClient
{
- /**
+ /*
* cached results of REST /info/divisions service, currently
- *
* <pre>
* {
- * "ENSEMBLFUNGI", "http://rest.ensemblgenomes.org"},
- * "ENSEMBLBACTERIA", "http://rest.ensemblgenomes.org"},
- * "ENSEMBLPROTISTS", "http://rest.ensemblgenomes.org"},
- * "ENSEMBLMETAZOA", "http://rest.ensemblgenomes.org"},
- * "ENSEMBLPLANTS", "http://rest.ensemblgenomes.org"},
- * "ENSEMBL", "http://rest.ensembl.org"
+ * { "ENSEMBLFUNGI", "http://rest.ensemblgenomes.org"},
+ * "ENSEMBLBACTERIA", "http://rest.ensemblgenomes.org"},
+ * "ENSEMBLPROTISTS", "http://rest.ensemblgenomes.org"},
+ * "ENSEMBLMETAZOA", "http://rest.ensemblgenomes.org"},
+ * "ENSEMBLPLANTS", "http://rest.ensemblgenomes.org"},
+ * "ENSEMBL", "http://rest.ensembl.org" }
* }
* </pre>
- *
* The values for EnsemblGenomes are retrieved by a REST call, that for
* Ensembl is added programmatically for convenience of lookup
*/
try
{
@SuppressWarnings("unchecked")
- Iterator<Object> rvals = (Iterator<Object>) getJSON(
- getDivisionsUrl(ensemblGenomesDomain), null, -1,
- MODE_ITERATOR, null);
+ Iterator<Object> rvals = (Iterator<Object>) getJSON(getDivisionsUrl(ensemblGenomesDomain), null, -1, MODE_ITERATOR, null);
if (rvals == null)
- {
- return;
- }
+ return;
while (rvals.hasNext())
{
String division = rvals.next().toString();
*
* @return
*/
- public Set<String> getDivisions()
- {
+ public Set<String> getDivisions() {
if (divisions == null)
{
fetchDivisions();
import jalview.bin.Cache;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.GeneLociI;
+import jalview.datamodel.GeneLocus;
+import jalview.datamodel.Mapping;
import jalview.util.MapList;
import java.io.IOException;
fromEnd });
List<int[]> toRange = Collections.singletonList(new int[] { toStart,
toEnd });
- final MapList map = new MapList(fromRange, toRange, 1, 1);
- return new GeneLociI()
- {
-
- @Override
- public String getSpeciesId()
- {
- return species == null ? "" : species;
- }
-
- @Override
- public String getAssemblyId()
- {
- return assembly;
- }
-
- @Override
- public String getChromosomeId()
- {
- return chromosome;
- }
-
- @Override
- public MapList getMap()
- {
- return map;
- }
- };
+ final Mapping map = new Mapping(
+ new MapList(fromRange, toRange, 1, 1));
+ return new GeneLocus(species == null ? "" : species, assembly,
+ chromosome, map);
} catch (NullPointerException | NumberFormatException e)
{
Cache.log.error("Error looking up gene loci: " + e.getMessage());
import jalview.datamodel.AlignmentI;
import jalview.datamodel.DBRefSource;
import jalview.datamodel.GeneLociI;
+import jalview.datamodel.GeneLocus;
+import jalview.datamodel.Mapping;
import jalview.util.MapList;
import java.io.IOException;
import org.json.simple.parser.ParseException;
+/**
+ * A client for the Ensembl REST service /map endpoint, to convert from
+ * coordinates of one genome assembly to another.
+ * <p>
+ * Note that species and assembly identifiers passed to this class must be valid
+ * in Ensembl. They are not case sensitive.
+ *
+ * @author gmcarstairs
+ * @see https://rest.ensembl.org/documentation/info/assembly_map
+ * @see https://rest.ensembl.org/info/assembly/human?content-type=text/xml
+ * @see https://rest.ensembl.org/info/species?content-type=text/xml
+ */
public class EnsemblMap extends EnsemblRestClient
{
private static final String MAPPED = "mapped";
final String chr = chromosome;
List<int[]> fromRange = Collections.singletonList(new int[] { 1,
fromEnd });
- final MapList map = new MapList(fromRange, regions, 1, 1);
- return new GeneLociI()
- {
-
- @Override
- public String getSpeciesId()
- {
- return species == null ? "" : species;
- }
-
- @Override
- public String getAssemblyId()
- {
- return as;
- }
-
- @Override
- public String getChromosomeId()
- {
- return chr;
- }
-
- @Override
- public MapList getMap()
- {
- return map;
- }
- };
+ Mapping mapping = new Mapping(new MapList(fromRange, regions, 1, 1));
+ return new GeneLocus(species == null ? "" : species, as, chr,
+ mapping);
} catch (IOException | ParseException | NumberFormatException e)
{
// ignore
import jalview.datamodel.AlignmentI;
import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceI;
-import jalview.util.Platform;
import java.util.ArrayList;
import java.util.List;
* or ENSMUSP or similar for other species
* or CCDSnnnnn.nn with at least 3 digits
*/
- private static Regex ACCESSION_REGEX;
+ private static final Regex ACCESSION_REGEX = new Regex(
+ "(ENS([A-Z]{3}|)P[0-9]{11}$)" + "|" + "(CCDS[0-9.]{3,}$)");
/**
* Default constructor (to use rest.ensembl.org)
@Override
public Regex getAccessionValidator()
{
- if (ACCESSION_REGEX == null)
- {
- ACCESSION_REGEX = Platform.newRegex(
- "(ENS([A-Z]{3}|)P[0-9]{11}$)" + "|" + "(CCDS[0-9.]{3,}$)", null);
- }
return ACCESSION_REGEX;
}
*/
package jalview.ext.ensembl;
-import jalview.util.Platform;
-import jalview.util.StringUtils;
-
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import org.json.simple.parser.ParseException;
+import jalview.util.Platform;
+import jalview.util.StringUtils;
+
/**
* Base class for Ensembl REST service clients
*
private static final String REST_CHANGE_LOG = "https://github.com/Ensembl/ensembl-rest/wiki/Change-log";
- private static final Map<String, EnsemblData> domainData;
+ private static Map<String, EnsemblData> domainData;
private final static long AVAILABILITY_RETEST_INTERVAL = 10000L; // 10 seconds
InputStream response = connection.getInputStream();
- Platform.timeCheck(null, Platform.TIME_MARK);
+ // Platform.timeCheck(null, Platform.TIME_MARK);
Object ret = Platform.parseJSON(response);
- Platform.timeCheck("EnsemblRestClient.getJSON " + url,
- Platform.TIME_MARK);
+ // Platform.timeCheck("EnsemblRestClient.getJSON " + url,
+ // Platform.TIME_MARK);
return ret;
}
*/
package jalview.ext.ensembl;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.json.simple.parser.ParseException;
+
import jalview.analysis.AlignmentUtils;
import jalview.analysis.Dna;
import jalview.bin.Cache;
import jalview.util.DBRefUtils;
import jalview.util.IntRangeComparator;
import jalview.util.MapList;
-import jalview.util.Platform;
-
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import org.json.simple.parser.ParseException;
/**
* Base class for Ensembl sequence fetchers
EnsemblFeatures gffFetcher = new EnsemblFeatures(getDomain());
EnsemblFeatureType[] features = getFeaturesToFetch();
- Platform.timeCheck("ESP.getsequencerec1", Platform.TIME_MARK);
-
+ // Platform.timeCheck("ESP.getsequencerec1", Platform.TIME_MARK);
AlignmentI geneFeatures = gffFetcher.getSequenceRecords(accId,
features);
genomicSequence = geneFeatures.getSequenceAt(0);
}
- Platform.timeCheck("ESP.getsequencerec2", Platform.TIME_MARK);
+ // Platform.timeCheck("ESP.getsequencerec2", Platform.TIME_MARK);
if (genomicSequence != null)
{
* fetch and map protein product, and add it as a cross-reference
* of the retrieved sequence
*/
- Platform.timeCheck("ESP.transferFeatures", Platform.TIME_MARK);
+ // Platform.timeCheck("ESP.transferFeatures", Platform.TIME_MARK);
addProteinProduct(querySeq);
}
}
System.err.println(
"Error transferring Ensembl features: " + e.getMessage());
}
- Platform.timeCheck("ESP.addfeat done", Platform.TIME_MARK);
+ // Platform.timeCheck("ESP.addfeat done", Platform.TIME_MARK);
}
/**
* copy exon features to protein, compute peptide variants from dna
* variants and add as features on the protein sequence ta-da
*/
- AlignmentUtils.computeProteinFeatures(querySeq, proteinSeq,
- mapList);
+ // JAL-3187 render on the fly instead
+ // AlignmentUtils.computeProteinFeatures(querySeq, proteinSeq, mapList);
}
} catch (Exception e)
{
protected void getCrossReferences(SequenceI seq)
{
- Platform.timeCheck("ESP. getdataseq ", Platform.TIME_MARK);
+ // Platform.timeCheck("ESP. getdataseq ", Platform.TIME_MARK);
while (seq.getDatasetSequence() != null)
seq = seq.getDatasetSequence();
}
- Platform.timeCheck("ESP. getxref ", Platform.TIME_MARK);
+ // Platform.timeCheck("ESP. getxref ", Platform.TIME_MARK);
EnsemblXref xrefFetcher = new EnsemblXref(getDomain(), getDbSource(),
getEnsemblDataVersion());
seq.addDBRef(self);
- Platform.timeCheck("ESP. seqprox done ", Platform.TIME_MARK);
-
+ // Platform.timeCheck("ESP. seqprox done ", Platform.TIME_MARK);
}
/**
inProgress = false;
throw new JalviewException("ENSEMBL Rest API not available.");
}
- Platform.timeCheck("EnsemblSeqProx.fetchSeq ", Platform.TIME_MARK);
+ // Platform.timeCheck("EnsemblSeqProx.fetchSeq ", Platform.TIME_MARK);
List<SequenceI> seqs = parseSequenceJson(ids);
if (seqs == null)
- {
- return alignment;
- }
+ return alignment;
if (seqs.isEmpty())
{
* for now, assumes only one sequence returned; refactor if needed
* in future to handle a JSONArray with more than one
*/
-
- Platform.timeCheck("ENS seqproxy", Platform.TIME_MARK);
+ // Platform.timeCheck("ENS seqproxy", Platform.TIME_MARK);
Map<String, Object> val = (Map<String, Object>) getJSON(null, ids, -1, MODE_MAP, null);
if (val == null)
- {
- return null;
- }
+ return null;
Object s = val.get("desc");
String desc = s == null ? null : s.toString();
s = val.get("id");
System.err.println("Error processing JSON response: " + e.toString());
// ignore
}
- Platform.timeCheck("ENS seqproxy2", Platform.TIME_MARK);
+ // Platform.timeCheck("ENS seqproxy2", Platform.TIME_MARK);
return result;
}
String comp = complement.toString();
sf.setValue(Gff3Helper.ALLELES, comp);
sf.setDescription(comp);
-
- /*
- * replace value of "alleles=" in sf.ATTRIBUTES as well
- * so 'output as GFF' shows reverse complement alleles
- */
- String atts = sf.getAttributes();
- if (atts != null)
- {
- atts = atts.replace(Gff3Helper.ALLELES + "=" + alleles,
- Gff3Helper.ALLELES + "=" + comp);
- sf.setAttributes(atts);
- }
}
/**
return false;
}
-
- Platform.timeCheck("ESP. xfer " + sfs.size(), Platform.TIME_MARK);
-
+ // Platform.timeCheck("ESP. xfer " + sfs.size(), Platform.TIME_MARK);
boolean result = transferFeatures(sfs, targetSequence, mapping,
accessionId);
import jalview.analysis.AlignmentUtils;
import jalview.bin.Cache;
import jalview.datamodel.DBRefSource;
-import jalview.util.Platform;
import jalview.ws.seqfetcher.DbSourceProxyImpl;
import com.stevesoft.pat.Regex;
// ensemblgenomes REST service merged to ensembl 9th April 2019
protected static final String DEFAULT_ENSEMBL_GENOMES_BASEURL = DEFAULT_ENSEMBL_BASEURL;
- private static Regex ACCESSION_REGEX;
+ /*
+ * accepts ENSG/T/E/P with 11 digits
+ * or ENSMUSP or similar for other species
+ * or CCDSnnnnn.nn with at least 3 digits
+ */
+ private static final Regex ACCESSION_REGEX = new Regex(
+ "(ENS([A-Z]{3}|)[GTEP]{1}[0-9]{11}$)" + "|"
+ + "(CCDS[0-9.]{3,}$)");
protected final String ensemblGenomesDomain;
@Override
public Regex getAccessionValidator()
{
- if (ACCESSION_REGEX == null)
- {
- /*
- * accepts ENSG/T/E/P with 11 digits
- * or ENSMUSP or similar for other species
- * or CCDSnnnnn.nn with at least 3 digits
- */
- ACCESSION_REGEX = Platform
- .newRegex("(ENS([A-Z]{3}|)[GTEP]{1}[0-9]{11}$)" + "|"
- + "(CCDS[0-9.]{3,}$)", null);
- }
return ACCESSION_REGEX;
}
package jalview.ext.htsjdk;
-import htsjdk.samtools.util.CloseableIterator;
-import htsjdk.variant.variantcontext.VariantContext;
-import htsjdk.variant.vcf.VCFFileReader;
-import htsjdk.variant.vcf.VCFHeader;
+import jalview.bin.Cache;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
+import htsjdk.samtools.util.CloseableIterator;
+import htsjdk.variant.variantcontext.VariantContext;
+import htsjdk.variant.vcf.VCFFileReader;
+import htsjdk.variant.vcf.VCFHeader;
+
/**
* A thin wrapper for htsjdk classes to read either plain, or compressed, or
* compressed and indexed VCF files
private static final String TBI_EXTENSION = ".tbi";
+ private static final String CSI_EXTENSION = ".csi";
+
private boolean indexed;
private VCFFileReader reader;
/**
- * Constructor given a raw or compressed VCF file or a (tabix) index file
+ * Constructor given a raw or compressed VCF file or a (csi or tabix) index file
* <p>
- * For now, file type is inferred from its suffix: .gz or .bgz for compressed
- * data, .tbi for an index file, anything else is assumed to be plain text
- * VCF.
+ * If the file path ends in ".tbi" or ".csi", <em>or</em> appending one of these
+ * extensions gives a valid file path, open as indexed, else as unindexed.
*
* @param f
* @throws IOException
*/
public VCFReader(String filePath) throws IOException
{
- if (filePath.endsWith(GZ))
+ indexed = false;
+ if (filePath.endsWith(TBI_EXTENSION)
+ || filePath.endsWith(CSI_EXTENSION))
{
- if (new File(filePath + TBI_EXTENSION).exists())
- {
- indexed = true;
- }
+ indexed = true;
+ filePath = filePath.substring(0, filePath.length() - 4);
}
- else if (filePath.endsWith(TBI_EXTENSION))
+ else if (new File(filePath + TBI_EXTENSION).exists())
+ {
+ indexed = true;
+ }
+ else if (new File(filePath + CSI_EXTENSION).exists())
{
indexed = true;
- filePath = filePath.substring(0, filePath.length() - 4);
}
- reader = new VCFFileReader(new File(filePath), indexed);
+ /*
+ * we pass the name of the unindexed file to htsjdk,
+ * with a flag to assert whether it is indexed
+ */
+ File file = new File(filePath);
+ if (file.exists())
+ {
+ reader = new VCFFileReader(file, indexed);
+ }
+ else
+ {
+ Cache.log.error("File not found: " + filePath);
+ }
}
@Override
public CloseableIterator<VariantContext> query(final String chrom,
final int start, final int end)
{
- if (reader == null) {
- return null;
- }
+ if (reader == null)
+ {
+ return null;
+ }
if (indexed)
{
return reader.query(chrom, start, end);
int vend = variant.getEnd();
// todo what is the undeprecated way to get
// the chromosome for the variant?
- if (chrom.equals(variant.getChr()) && (vstart <= end)
+ if (chrom.equals(variant.getContig()) && (vstart <= end)
&& (vend >= start))
{
return variant;
import java.awt.event.ComponentListener;
import java.io.File;
import java.net.URL;
-import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
+import java.util.StringTokenizer;
import java.util.Vector;
import org.jmol.adapter.smarter.SmarterJmolAdapter;
implements JmolStatusListener, JmolSelectionListener,
ComponentListener
{
+ private String lastMessage;
+
boolean allChainsSelected = false;
/*
String lastCommand;
- String lastMessage;
-
boolean loadedInline;
StringBuffer resetLastRes = new StringBuffer();
*/
private int _modelFileNameMap[];
- // ////////////////////////////////
- // /StructureListener
- // @Override
- public synchronized String[] getPdbFilex()
- {
- if (viewer == null)
- {
- return new String[0];
- }
- if (modelFileNames == null)
- {
- List<String> mset = new ArrayList<>();
- _modelFileNameMap = new int[viewer.ms.mc];
- String m = viewer.ms.getModelFileName(0);
- if (m != null)
- {
- String filePath = m;
- try
- {
- filePath = new File(m).getAbsolutePath();
- } catch (AccessControlException x)
- {
- // usually not allowed to do this in applet
- System.err.println(
- "jmolBinding: Using local file string from Jmol: " + m);
- }
- if (filePath.indexOf("/file:") != -1)
- {
- // applet path with docroot - discard as format won't match pdbfile
- filePath = m;
- }
- mset.add(filePath);
- _modelFileNameMap[0] = 0; // filename index for first model is always 0.
- }
- int j = 1;
- for (int i = 1; i < viewer.ms.mc; i++)
- {
- m = viewer.ms.getModelFileName(i);
- String filePath = m;
- if (m != null)
- {
- try
- {
- filePath = new File(m).getAbsolutePath();
- } catch (AccessControlException x)
- {
- // usually not allowed to do this in applet, so keep raw handle
- // System.err.println("jmolBinding: Using local file string from
- // Jmol: "+m);
- }
- }
-
- /*
- * add this model unless it is read from a structure file we have
- * already seen (example: 2MJW is an NMR structure with 10 models)
- */
- if (!mset.contains(filePath))
- {
- mset.add(filePath);
- _modelFileNameMap[j] = i; // record the model index for the filename
- j++;
- }
- }
- modelFileNames = mset.toArray(new String[mset.size()]);
- }
- return modelFileNames;
- }
-
@Override
public synchronized String[] getStructureFiles()
{
viewer.openStringInline(string);
}
- public void mouseOverStructure(int atomIndex, String strInfo)
+ protected void mouseOverStructure(int atomIndex, final String strInfo)
{
int pdbResNum;
int alocsep = strInfo.indexOf("^");
try
{
// recover PDB filename for the model hovered over.
- int mnumber = new Integer(mdlId).intValue() - 1;
+ int mnumber = Integer.valueOf(mdlId).intValue() - 1;
if (_modelFileNameMap != null)
{
int _mp = _modelFileNameMap.length - 1;
} catch (Exception e)
{
}
- ;
}
- if (lastMessage == null || !lastMessage.equals(strInfo))
+
+ /*
+ * highlight position on alignment(s); if some text is returned,
+ * show this as a second line on the structure hover tooltip
+ */
+ String label = getSsm().mouseOverStructure(pdbResNum, chainId,
+ pdbfilename);
+ if (label != null)
{
- getSsm().mouseOverStructure(pdbResNum, chainId, pdbfilename);
+ // change comma to pipe separator (newline token for Jmol)
+ label = label.replace(',', '|');
+ StringTokenizer toks = new StringTokenizer(strInfo, " ");
+ StringBuilder sb = new StringBuilder();
+ sb.append("select ").append(String.valueOf(pdbResNum)).append(":")
+ .append(chainId).append("/1");
+ sb.append(";set hoverLabel \"").append(toks.nextToken()).append(" ")
+ .append(toks.nextToken());
+ sb.append("|").append(label).append("\"");
+ evalStateCommand(sb.toString());
}
-
- lastMessage = strInfo;
}
public void notifyAtomHovered(int atomIndex, String strInfo, String data)
{
+ if (strInfo.equals(lastMessage))
+ {
+ return;
+ }
+ lastMessage = strInfo;
if (data != null)
{
System.err.println("Ignoring additional hover info: " + data
*/
package jalview.ext.paradise;
-import jalview.util.JSONUtils;
-import jalview.util.MessageManager;
-import jalview.ws.HttpClientUtils;
-
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.json.simple.parser.ContentHandler;
import org.json.simple.parser.ParseException;
+import jalview.util.JSONUtils;
+import jalview.util.MessageManager;
+import jalview.ws.HttpClientUtils;
+
/**
* simple methods for calling the various paradise RNA tools
*
public static Iterator<Reader> getRNAMLForPDBFileAsString(String pdbfile)
throws Exception
{
- List<NameValuePair> vals = new ArrayList<NameValuePair>();
+ List<NameValuePair> vals = new ArrayList<>();
vals.add(new BasicNameValuePair("tool", "rnaview"));
vals.add(new BasicNameValuePair("data", pdbfile));
vals.add(new BasicNameValuePair("output", "rnaml"));
// return processJsonResponseFor(HttpClientUtils.doHttpUrlPost(twoDtoolsURL,
// vals));
- ArrayList<Reader> readers = new ArrayList<Reader>();
+ ArrayList<Reader> readers = new ArrayList<>();
final BufferedReader postResponse = HttpClientUtils
.doHttpUrlPost(twoDtoolsURL, vals, 0, 0);
readers.add(postResponse);
}
- /**
- * @param respons
- * @return
- * @throws Exception
- */
public static Iterator<Reader> processJsonResponseFor(Reader respons)
throws Exception
{
- // BH 2019 never called?
+ org.json.simple.parser.JSONParser jp = new org.json.simple.parser.JSONParser();
try
{
- @SuppressWarnings("unchecked")
- final Iterator<Object> rvals = ((List<Object>) JSONUtils.parse(respons)).iterator();
- return new Iterator<Reader>()
- {
- @Override
- public boolean hasNext()
- {
- return rvals.hasNext();
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public Reader next()
- {
- Map<String, Object> val = (Map<String, Object>) rvals.next();
-
- Object sval = null;
- try
- {
- sval = val.get("2D");
- } catch (Exception x)
- {
- x.printStackTrace();
- }
- ;
- if (sval == null)
- {
- System.err.println(
- "DEVELOPER WARNING: Annotate3d didn't return a '2D' tag in its response. Consider checking output of server. Response was :"
- + val.toString());
-
- sval = "";
- }
- return new StringReader(sval.toString());
-
- }
-
- @Override
- public void remove()
- {
- throw new Error(
- MessageManager.getString("error.not_implemented_remove"));
-
- }
-
- @Override
- protected Object clone() throws CloneNotSupportedException
- {
- throw new CloneNotSupportedException(
- MessageManager.getString("error.not_implemented_clone"));
- }
-
- @Override
- public boolean equals(Object obj)
- {
- return super.equals(obj);
- }
-
- @Override
- protected void finalize() throws Throwable
- {
- while (rvals.hasNext())
- {
- rvals.next();
- }
- super.finalize();
- }
- };
+ final RvalsIterator rvals = new RvalsIterator(respons);
+ return rvals;
} catch (Exception foo)
{
throw new Exception(MessageManager.getString(
public static Iterator<Reader> getRNAMLForPDBId(String pdbid)
throws Exception
{
- List<NameValuePair> vals = new ArrayList<NameValuePair>();
+ List<NameValuePair> vals = new ArrayList<>();
vals.add(new BasicNameValuePair("tool", "rnaview"));
vals.add(new BasicNameValuePair("pdbid", pdbid));
vals.add(new BasicNameValuePair("output", "rnaml"));
+ pdbid + "&output=rnaml");
// return processJsonResponseFor(new
// InputStreamReader(geturl.openStream()));
- ArrayList<Reader> readers = new ArrayList<Reader>();
+ ArrayList<Reader> readers = new ArrayList<>();
readers.add(new InputStreamReader(geturl.openStream()));
return readers.iterator();
}
}
+
+class RvalsIterator implements Iterator, AutoCloseable
+{
+ private Iterator<Object> rvals;
+
+ @SuppressWarnings("unchecked")
+ protected RvalsIterator(Reader respons) throws IOException, ParseException
+ {
+ /*
+ * as in 2.11.1 (pre-JalviewJS)
+ */
+ // final JSONArray responses = (JSONArray) jp.parse(respons);
+ // this.rvals = responses.iterator();
+
+ /*
+ * as in JalviewJS (with comment that this code is never called)
+ */
+ this.rvals = ((List<Object>) JSONUtils.parse(respons)).iterator();
+ }
+
+ @Override
+ public boolean hasNext()
+ {
+ return rvals.hasNext();
+ }
+
+ @Override
+ public Reader next()
+ {
+ // JSONObject val = (JSONObject) rvals.next(); // 2.11.1
+ @SuppressWarnings("unchecked")
+ Map<String, Object> val = (Map<String, Object>) rvals.next();
+
+ Object sval = null;
+ try
+ {
+ sval = val.get("2D");
+ } catch (Exception x)
+ {
+ x.printStackTrace();
+ }
+
+ if (sval == null)
+ {
+ System.err.println(
+ "DEVELOPER WARNING: Annotate3d didn't return a '2D' tag in its response. Consider checking output of server. Response was :"
+ + val.toString());
+
+ sval = "";
+ }
+ // 2.11.1:
+ // return new StringReader(
+ // (sval instanceof JSONObject) ? ((JSONObject) sval).toString()
+ // : sval.toString());
+ return new StringReader(sval.toString());
+
+ }
+
+ @Override
+ public void remove()
+ {
+ throw new Error(
+ MessageManager.getString("error.not_implemented_remove"));
+
+ }
+
+ @Override
+ protected Object clone() throws CloneNotSupportedException
+ {
+ throw new CloneNotSupportedException(
+ MessageManager.getString("error.not_implemented_clone"));
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ return super.equals(obj);
+ }
+
+ @Override
+ public void close()
+ {
+ while (rvals.hasNext())
+ {
+ rvals.next();
+ }
+ }
+}
import jalview.api.SequenceRenderer;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.MappedFeatures;
import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceI;
+import jalview.gui.Desktop;
import jalview.renderer.seqfeatures.FeatureColourFinder;
import jalview.structure.StructureMapping;
import jalview.structure.StructureMappingcommandSet;
* delimited). If length limit issues arise, refactor to return one color
* command per colour.
*/
- List<String> commands = new ArrayList<String>();
+ List<String> commands = new ArrayList<>();
StringBuilder sb = new StringBuilder(256);
boolean firstColour = true;
for (Object key : colourMap.keySet())
AlignViewportI viewport = viewPanel.getAlignViewport();
HiddenColumns cs = viewport.getAlignment().getHiddenColumns();
AlignmentI al = viewport.getAlignment();
- Map<Object, AtomSpecModel> colourMap = new LinkedHashMap<Object, AtomSpecModel>();
+ Map<Object, AtomSpecModel> colourMap = new LinkedHashMap<>();
Color lastColour = null;
for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
{
if (startPos != -1)
{
- addColourRange(colourMap, lastColour, pdbfnum, startPos,
+ addAtomSpecRange(colourMap, lastColour, pdbfnum, startPos,
lastPos, lastChain);
}
startPos = pos;
// final colour range
if (lastColour != null)
{
- addColourRange(colourMap, lastColour, pdbfnum, startPos,
+ addAtomSpecRange(colourMap, lastColour, pdbfnum, startPos,
lastPos, lastChain);
}
// break;
}
/**
- * Helper method to add one contiguous colour range to the colour map.
+ * Helper method to add one contiguous range to the AtomSpec model for the given
+ * value (creating the model if necessary). As used by Jalview, {@code value} is
+ * <ul>
+ * <li>a colour, when building a 'colour structure by sequence' command</li>
+ * <li>a feature value, when building a 'set Chimera attributes from features'
+ * command</li>
+ * </ul>
*
* @param map
- * @param key
+ * @param value
* @param model
* @param startPos
* @param endPos
* @param chain
*/
- protected static void addColourRange(Map<Object, AtomSpecModel> map,
- Object key, int model, int startPos, int endPos, String chain)
+ protected static void addAtomSpecRange(Map<Object, AtomSpecModel> map,
+ Object value, int model, int startPos, int endPos, String chain)
{
/*
* Get/initialize map of data for the colour
*/
- AtomSpecModel atomSpec = map.get(key);
+ AtomSpecModel atomSpec = map.get(value);
if (atomSpec == null)
{
atomSpec = new AtomSpecModel();
- map.put(key, atomSpec);
+ map.put(value, atomSpec);
}
atomSpec.addRange(model, startPos, endPos, chain);
StructureSelectionManager ssm, String[] files, SequenceI[][] seqs,
AlignmentViewPanel viewPanel)
{
- Map<String, Map<Object, AtomSpecModel>> theMap = new LinkedHashMap<String, Map<Object, AtomSpecModel>>();
+ Map<String, Map<Object, AtomSpecModel>> theMap = new LinkedHashMap<>();
FeatureRenderer fr = viewPanel.getFeatureRenderer();
if (fr == null)
return theMap;
}
+ AlignViewportI viewport = viewPanel.getAlignViewport();
List<String> visibleFeatures = fr.getDisplayedFeatureTypes();
- if (visibleFeatures.isEmpty())
+
+ /*
+ * if alignment is showing features from complement, we also transfer
+ * these features to the corresponding mapped structure residues
+ */
+ boolean showLinkedFeatures = viewport.isShowComplementFeatures();
+ List<String> complementFeatures = new ArrayList<>();
+ FeatureRenderer complementRenderer = null;
+ if (showLinkedFeatures)
+ {
+ AlignViewportI comp = fr.getViewport().getCodingComplement();
+ if (comp != null)
+ {
+ complementRenderer = Desktop.getAlignFrameFor(comp)
+ .getFeatureRenderer();
+ complementFeatures = complementRenderer.getDisplayedFeatureTypes();
+ }
+ }
+ if (visibleFeatures.isEmpty() && complementFeatures.isEmpty())
{
return theMap;
}
{
final SequenceI seq = seqs[pdbfnum][seqNo];
int sp = alignment.findIndex(seq);
- if (mapping[m].getSequence() == seq && sp > -1)
+ StructureMapping structureMapping = mapping[m];
+ if (structureMapping.getSequence() == seq && sp > -1)
{
/*
* found a sequence with a mapping to a structure;
* now scan its features
*/
- SequenceI asp = alignment.getSequenceAt(sp);
-
- scanSequenceFeatures(visibleFeatures, mapping[m], asp, theMap,
- pdbfnum);
+ if (!visibleFeatures.isEmpty())
+ {
+ scanSequenceFeatures(visibleFeatures, structureMapping, seq,
+ theMap, pdbfnum);
+ }
+ if (showLinkedFeatures)
+ {
+ scanComplementFeatures(complementRenderer, structureMapping,
+ seq, theMap, pdbfnum);
+ }
}
}
}
}
/**
- * Inspect features on the sequence; for each feature that is visible,
- * determine its mapped ranges in the structure (if any) according to the
- * given mapping, and add them to the map
+ * Scans visible features in mapped positions of the CDS/peptide complement, and
+ * adds any found to the map of attribute values/structure positions
+ *
+ * @param complementRenderer
+ * @param structureMapping
+ * @param seq
+ * @param theMap
+ * @param modelNumber
+ */
+ protected static void scanComplementFeatures(
+ FeatureRenderer complementRenderer,
+ StructureMapping structureMapping, SequenceI seq,
+ Map<String, Map<Object, AtomSpecModel>> theMap, int modelNumber)
+ {
+ /*
+ * for each sequence residue mapped to a structure position...
+ */
+ for (int seqPos : structureMapping.getMapping().keySet())
+ {
+ /*
+ * find visible complementary features at mapped position(s)
+ */
+ MappedFeatures mf = complementRenderer
+ .findComplementFeaturesAtResidue(seq, seqPos);
+ if (mf != null)
+ {
+ for (SequenceFeature sf : mf.features)
+ {
+ String type = sf.getType();
+
+ /*
+ * Don't copy features which originated from Chimera
+ */
+ if (JalviewChimeraBinding.CHIMERA_FEATURE_GROUP
+ .equals(sf.getFeatureGroup()))
+ {
+ continue;
+ }
+
+ /*
+ * record feature 'value' (score/description/type) as at the
+ * corresponding structure position
+ */
+ List<int[]> mappedRanges = structureMapping
+ .getPDBResNumRanges(seqPos, seqPos);
+
+ if (!mappedRanges.isEmpty())
+ {
+ String value = sf.getDescription();
+ if (value == null || value.length() == 0)
+ {
+ value = type;
+ }
+ float score = sf.getScore();
+ if (score != 0f && !Float.isNaN(score))
+ {
+ value = Float.toString(score);
+ }
+ Map<Object, AtomSpecModel> featureValues = theMap.get(type);
+ if (featureValues == null)
+ {
+ featureValues = new HashMap<>();
+ theMap.put(type, featureValues);
+ }
+ for (int[] range : mappedRanges)
+ {
+ addAtomSpecRange(featureValues, value, modelNumber, range[0],
+ range[1], structureMapping.getChain());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Inspect features on the sequence; for each feature that is visible, determine
+ * its mapped ranges in the structure (if any) according to the given mapping,
+ * and add them to the map.
*
* @param visibleFeatures
* @param mapping
String type = sf.getType();
/*
- * Only copy visible features, don't copy any which originated
- * from Chimera, and suppress uninteresting ones (e.g. RESNUM)
+ * Don't copy features which originated from Chimera
*/
- boolean isFromViewer = JalviewChimeraBinding.CHIMERA_FEATURE_GROUP
- .equals(sf.getFeatureGroup());
- if (isFromViewer)
+ if (JalviewChimeraBinding.CHIMERA_FEATURE_GROUP
+ .equals(sf.getFeatureGroup()))
{
continue;
}
+
List<int[]> mappedRanges = mapping.getPDBResNumRanges(sf.getBegin(),
sf.getEnd());
Map<Object, AtomSpecModel> featureValues = theMap.get(type);
if (featureValues == null)
{
- featureValues = new HashMap<Object, AtomSpecModel>();
+ featureValues = new HashMap<>();
theMap.put(type, featureValues);
}
for (int[] range : mappedRanges)
{
- addColourRange(featureValues, value, modelNumber, range[0],
+ addAtomSpecRange(featureValues, value, modelNumber, range[0],
range[1], mapping.getChain());
}
}
protected static List<String> buildSetAttributeCommands(
Map<String, Map<Object, AtomSpecModel>> featureMap)
{
- List<String> commands = new ArrayList<String>();
+ List<String> commands = new ArrayList<>();
for (String featureType : featureMap.keySet())
{
String attributeName = makeAttributeName(featureType);
*/
public SequenceOntology()
{
- termsFound = new ArrayList<>();
- termsNotFound = new ArrayList<>();
- termsByDescription = new HashMap<>();
- termIsA = new HashMap<>();
+ termsFound = new ArrayList<String>();
+ termsNotFound = new ArrayList<String>();
+ termsByDescription = new HashMap<String, Term>();
+ termIsA = new HashMap<Term, List<Term>>();
loadOntologyZipFile("so-xp-simple.obo");
}
*/
protected synchronized void findParents(Term childTerm)
{
- List<Term> result = new ArrayList<>();
+ List<Term> result = new ArrayList<Term>();
for (Triple triple : ontology.getTriples(childTerm, null, isA))
{
Term parent = triple.getObject();
import jalview.api.AlignViewControllerI;
import jalview.api.AlignViewportI;
import jalview.api.AlignmentViewPanel;
-//from JalviewLite imports import jalview.api.FeatureRenderer;
import jalview.api.FeatureSettingsControllerI;
+import jalview.api.FeatureSettingsModelI;
import jalview.api.SplitContainerI;
import jalview.api.ViewStyleI;
import jalview.api.analysis.SimilarityParamsI;
}
viewport = new AlignViewport(al, hiddenColumns, sequenceSetId, viewId);
+ // BH! new alignPanel must come later
+ // alignPanel = new AlignmentPanel(this, viewport);
+ // addAlignmentPanel(alignPanel, true);
init();
}
{
viewport.hideSequence(hiddenSeqs);
}
- init();
+ // BH! new alignPanel must come later
+ //alignPanel = new AlignmentPanel(this, viewport);
+ //addAlignmentPanel(alignPanel, true);
+init();
}
/**
{
viewport = ap.av;
alignPanel = ap;
+ // BH! adding must come later
+ // addAlignmentPanel(ap, false);
init();
}
*/
void init()
{
+ // BH! Here is where we create the panel; note the sequence
boolean newPanel = (alignPanel == null);
viewport.setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
if (newPanel)
addAlignmentPanel(alignPanel, newPanel);
// setBackground(Color.white); // BH 2019
-
+
+ // BH meaning "without display, one way or another"
if (!Jalview.isHeadlessMode())
{
progressBar = new ProgressBar(this.statusPanel, this.statusBar);
// modifyPID.setEnabled(false);
}
- String sortby = jalview.bin.Cache.getDefault(Preferences.SORT_ALIGNMENT,
+ String sortby = Cache.getDefault(Preferences.SORT_ALIGNMENT,
"No sort");
if (sortby.equals("Id"))
sortPairwiseMenuItem_actionPerformed(null);
}
+ alignPanel.av
+ .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
+
setMenusFromViewport(viewport);
buildSortByAnnotationScoresMenu();
calculateTree.addActionListener(new ActionListener()
if (Desktop.getDesktopPane() != null)
{
this.setDropTarget(new java.awt.dnd.DropTarget(this, this));
- PropertyChangeListener serviceListener = (Platform.isJS() ? null
- : addServiceListeners());
- addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
+ if (!Platform.isJS())
{
- @Override
- public void internalFrameClosed(
- javax.swing.event.InternalFrameEvent evt)
- {
- // System.out.println("deregistering discoverer listener");
- if (serviceListener != null)
- {
- Desktop.getInstance().removeJalviewPropertyChangeListener(
- "services", serviceListener);
- }
- closeMenuItem_actionPerformed(true);
- }
- });
-
+ addServiceListeners();
+ }
setGUINucleotide();
}
{
ap.av.getAlignment().padGaps();
}
+ // BH! important option for JalviewJS
if (Jalview.getInstance().getStartCalculations())
{
ap.av.updateConservation(ap);
}
/* Set up intrinsic listeners for dynamically generated GUI bits. */
- private PropertyChangeListener addServiceListeners()
+ private void addServiceListeners()
{
- PropertyChangeListener serviceListener = new PropertyChangeListener()
+ PropertyChangeListener thisListener;
+ Desktop.getInstance().addJalviewPropertyChangeListener("services",
+ thisListener = new PropertyChangeListener()
{
@Override
public void propertyChange(PropertyChangeEvent evt)
@Override
public void run()
{
- System.err.println("Rebuild WS Menu for service change");
+ System.err.println(
+ "Rebuild WS Menu for service change");
BuildWebServiceMenu();
}
});
}
}
- };
-
- Desktop.getInstance().addJalviewPropertyChangeListener("services",
- serviceListener);
+ });
+ addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
+ {
+ @Override
+ public void internalFrameClosed(
+ javax.swing.event.InternalFrameEvent evt)
+ {
+ // System.out.println("deregistering discoverer listener");
+ Desktop.getInstance().removeJalviewPropertyChangeListener("services",
+ thisListener);
+ closeMenuItem_actionPerformed(true);
+ }
+ });
// Finally, build the menu once to get current service state
new Thread(new Runnable()
{
BuildWebServiceMenu();
}
}).start();
- return serviceListener;
}
/**
scaleLeft.setVisible(av.getWrapAlignment());
scaleRight.setVisible(av.getWrapAlignment());
annotationPanelMenuItem.setState(av.isShowAnnotation());
- /*
- * Show/hide annotations only enabled if annotation panel is shown
- */
+ // Show/hide annotations only enabled if annotation panel is shown
+
syncAnnotationMenuItems();
viewBoxesMenuItem.setSelected(av.getShowBoxes());
DataSourceType protocol = fileName.startsWith("http:")
? DataSourceType.URL
: DataSourceType.FILE;
- loader.loadFile(viewport,
- (fileObject == null ? fileName : fileObject), protocol,
- currentFileFormat);
+ if (fileObject == null)
+ {
+ loader.LoadFile(fileName, protocol, currentFileFormat);
+ }
+ else
+ {
+ loader.loadFile(null, fileObject, protocol, currentFileFormat);
+ }
}
else
{
if (fileObject == null)
{
-
+// BH! Q: What about https?
DataSourceType protocol = (fileName.startsWith("http:")
? DataSourceType.URL
: DataSourceType.FILE);
- newframe = loader.loadFileWaitTillLoaded(fileName, protocol,
+ newframe = loader.LoadFileWaitTillLoaded(fileName, protocol,
currentFileFormat);
}
else
try
{
String tempFilePath = doBackup ? backupfiles.getTempFilePath() : file;
- PrintWriter out = new PrintWriter(
+ PrintWriter out = new PrintWriter(
new FileWriter(tempFilePath));
out.print(output);
if (closeAllTabs)
{
+ if (featureSettings != null && featureSettings.isOpen())
+ {
+ featureSettings.close();
+ featureSettings = null;
+ }
/*
* this will raise an INTERNAL_FRAME_CLOSED event and this method will
* be called recursively, with the frame now in 'closed' state
newGraphGroups.add(q, null);
}
newGraphGroups.set(newann.graphGroup,
- new Integer(++fgroup));
+ Integer.valueOf(++fgroup));
}
newann.graphGroup = newGraphGroups.get(newann.graphGroup)
.intValue();
newGraphGroups.add(q, null);
}
newGraphGroups.set(newann.graphGroup,
- new Integer(++fgroup));
+ Integer.valueOf(++fgroup));
}
newann.graphGroup = newGraphGroups.get(newann.graphGroup)
.intValue();
Runnable okAction = new Runnable()
{
- @Override
- public void run()
- {
- SequenceI[] cut = sg.getSequences()
- .toArray(new SequenceI[sg.getSize()]);
-
- addHistoryItem(new EditCommand(
- MessageManager.getString("label.cut_sequences"), Action.CUT,
- cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
- viewport.getAlignment()));
-
- viewport.setSelectionGroup(null);
- viewport.sendSelection();
- viewport.getAlignment().deleteGroup(sg);
-
- viewport.firePropertyChange("alignment", null,
- viewport.getAlignment().getSequences());
- if (viewport.getAlignment().getHeight() < 1)
- {
- try
- {
- AlignFrame.this.setClosed(true);
- } catch (Exception ex)
- {
- }
- }
- }};
+ @Override
+ public void run()
+ {
+ SequenceI[] cut = sg.getSequences()
+ .toArray(new SequenceI[sg.getSize()]);
+
+ addHistoryItem(new EditCommand(
+ MessageManager.getString("label.cut_sequences"), Action.CUT,
+ cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
+ viewport.getAlignment()));
+
+ viewport.setSelectionGroup(null);
+ viewport.sendSelection();
+ viewport.getAlignment().deleteGroup(sg);
+
+ viewport.firePropertyChange("alignment", null,
+ viewport.getAlignment().getSequences());
+ if (viewport.getAlignment().getHeight() < 1)
+ {
+ try
+ {
+ AlignFrame.this.setClosed(true);
+ } catch (Exception ex)
+ {
+ }
+ }
+ }};
/*
* If the cut affects all sequences, prompt for confirmation
boolean wholeHeight = sg.getSize() == viewport.getAlignment().getHeight();
boolean wholeWidth = (((sg.getEndRes() - sg.getStartRes())
+ 1) == viewport.getAlignment().getWidth()) ? true : false;
- if (wholeHeight && wholeWidth)
- {
- JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.getDesktopPane());
- dialog.setResponseHandler(0, okAction); // 0 = OK_OPTION
- Object[] options = new Object[] { MessageManager.getString("action.ok"),
- MessageManager.getString("action.cancel") };
- dialog.showDialog(MessageManager.getString("warn.delete_all"),
- MessageManager.getString("label.delete_all"),
- JvOptionPane.DEFAULT_OPTION, JvOptionPane.PLAIN_MESSAGE, null,
- options, options[0]);
- } else
- {
- okAction.run();
- }
+ if (wholeHeight && wholeWidth)
+ {
+ JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.getDesktopPane());
+ dialog.setResponseHandler(0, okAction); // 0 = OK_OPTION
+ Object[] options = new Object[] { MessageManager.getString("action.ok"),
+ MessageManager.getString("action.cancel") };
+ dialog.showDialog(MessageManager.getString("warn.delete_all"),
+ MessageManager.getString("label.delete_all"),
+ JvOptionPane.DEFAULT_OPTION, JvOptionPane.PLAIN_MESSAGE, null,
+ options, options[0]);
+ } else
+ {
+ okAction.run();
+ }
}
/**
@Override
public void featureSettings_actionPerformed(ActionEvent e)
{
+ showFeatureSettingsUI();
+ }
+
+ @Override
+ public FeatureSettingsControllerI showFeatureSettingsUI()
+ {
if (featureSettings != null)
{
- featureSettings.close();
+ featureSettings.closeOldSettings();
featureSettings = null;
}
if (!showSeqFeatures.isSelected())
showSeqFeatures_actionPerformed(null);
}
featureSettings = new FeatureSettings(this);
+ return featureSettings;
}
/**
// associating PDB files which have no IDs.
for (SequenceI toassoc : (SequenceI[]) fm[2])
{
+ // BH! check
PDBEntry pe = AssociatePdbFileWithSeq
.associatePdbWithSeq(fm[0].toString(),
(DataSourceType) fm[1], toassoc, false);
{
if (parseFeaturesFile(file, sourceType))
{
+ SplitFrame splitFrame = (SplitFrame) getSplitViewContainer();
+ if (splitFrame != null)
+ {
+ splitFrame.repaint();
+ }
+ else
+ {
alignPanel.paintAlignment(true, true);
}
}
+ }
else
{
- new FileLoader().loadFile(viewport, file, sourceType, format);
+ if (file instanceof File) {
+ new FileLoader().loadFile(viewport, (File) file, sourceType, format);
+ } else {
+ new FileLoader().LoadFile(viewport, (String) file, sourceType, format);
+ }
}
}
}
viewport = alignPanel.av;
avc.setViewportAndAlignmentPanel(viewport, alignPanel);
setMenusFromViewport(viewport);
+ if (featureSettings != null && featureSettings.isOpen()
+ && featureSettings.fr.getViewport() != viewport)
+ {
+ if (viewport.isShowSequenceFeatures())
+ {
+ // refresh the featureSettings to reflect UI change
+ showFeatureSettingsUI();
+ }
+ else
+ {
+ // close feature settings for this view.
+ featureSettings.close();
+ }
+ }
+
}
/*
@Override
public void finished()
{
+
+ for (FeatureSettingsModelI srcSettings : dbRefFetcher
+ .getFeatureSettingsModels())
+ {
+
+ alignPanel.av.mergeFeaturesStyle(srcSettings);
+ }
AlignFrame.this.setMenusForViewport();
}
});
}
if (otherdb.size() == 1)
{
+ // BH cleaner code
DbSourceProxy src = otherdb.get(0);
DbSourceProxy[] dassource = new DbSourceProxy[] {
src };
@Override
public void finished()
{
+ FeatureSettingsModelI srcSettings = dassource[0]
+ .getFeatureColourScheme();
+ alignPanel.av.mergeFeaturesStyle(
+ srcSettings);
AlignFrame.this.setMenusForViewport();
}
});
}
+
+ private Rectangle lastFeatureSettingsBounds = null;
+ @Override
+ public void setFeatureSettingsGeometry(Rectangle bounds)
+ {
+ lastFeatureSettingsBounds = bounds;
+ }
+
+ @Override
+ public Rectangle getFeatureSettingsGeometry()
+ {
+ return lastFeatureSettingsBounds;
+ }
+
/**
* BH 2019 from JalviewLite
*
import jalview.structure.SelectionSource;
import jalview.structure.StructureSelectionManager;
import jalview.structure.VamsasSource;
+import jalview.util.ColorUtils;
import jalview.util.MessageManager;
import jalview.viewmodel.AlignmentViewport;
import jalview.ws.params.AutoCalcSetting;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Rectangle;
+import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
boolean antiAlias = false;
- private Rectangle explodedGeometry;
+ private Rectangle explodedGeometry = null;
- private String viewName;
+ private String viewName = null;
/*
* Flag set true on the view that should 'gather' multiple views of the same
*/
private void applyViewProperties()
{
- antiAlias = Cache.getDefault(Preferences.ANTI_ALIAS, false);
+ // BH! using final static strings here because we also use these in
+ // JS version startup api
+ // BH was false
+ antiAlias = Cache.getDefault(Preferences.ANTI_ALIAS, true);
viewStyle.setShowJVSuffix(
Cache.getDefault(Preferences.SHOW_JVSUFFIX, true));
schemeName = Cache.getDefault(Preferences.DEFAULT_COLOUR,
ResidueColourScheme.NONE);
}
- ColourSchemeI colourScheme = ColourSchemeProperty.getColourScheme(this,
- alignment, schemeName);
+ ColourSchemeI colourScheme = ColourSchemeProperty
+ .getColourScheme(this, alignment, schemeName);
residueShading = new ResidueShader(colourScheme);
if (colourScheme instanceof UserColourScheme)
public static void openLinkedAlignmentAs(AlignFrame thisFrame,
AlignmentI thisAlignment, AlignmentI al, String title, int mode)
{
- /*
- * Identify protein and dna alignments. Make a copy of this one if opening
- * in a new split pane.
- */
+ // BH: thisAlignment is already a copy if mode == SPLIT_FRAME
+ // Identify protein and dna alignments. Make a copy of this one if opening
+ // in a new split pane.
+
AlignmentI protein = al.isNucleotide() ? thisAlignment : al;
AlignmentI cdna = al.isNucleotide() ? al : thisAlignment;
try
{
- newAlignFrame.setMaximum(jalview.bin.Cache
- .getDefault(Preferences.SHOW_FULLSCREEN, false));
+ newAlignFrame.setMaximum(Cache.getDefault(Preferences.SHOW_FULLSCREEN, false));
} catch (java.beans.PropertyVetoException ex)
{
}
if (ap != null)
{
// modify GUI elements to reflect geometry change
- Dimension idw = getAlignPanel().getIdPanel().getIdCanvas()
- .getPreferredSize();
+ Dimension idw = ap.getIdPanel().getIdCanvas().getPreferredSize();
idw.width = i;
- getAlignPanel().getIdPanel().getIdCanvas().setPreferredSize(idw);
+ ap.getIdPanel().getIdCanvas().setPreferredSize(idw);
}
}
@Override
public void applyFeaturesStyle(FeatureSettingsModelI featureSettings)
{
+ transferFeaturesStyles(featureSettings, false);
+ }
+
+ /**
+ * Applies the supplied feature settings descriptor to currently known features.
+ * This supports an 'initial configuration' of feature colouring based on a
+ * preset or user favourite. This may then be modified in the usual way using
+ * the Feature Settings dialogue.
+ *
+ * @param featureSettings
+ */
+ @Override
+ public void mergeFeaturesStyle(FeatureSettingsModelI featureSettings)
+ {
+ transferFeaturesStyles(featureSettings, true);
+ }
+
+ /**
+ * when mergeOnly is set, then group and feature visibility or feature colours
+ * are not modified for features and groups already known to the feature
+ * renderer. Feature ordering is always adjusted, and transparency is always set
+ * regardless.
+ *
+ * @param featureSettings
+ * @param mergeOnly
+ */
+ private void transferFeaturesStyles(FeatureSettingsModelI featureSettings,
+ boolean mergeOnly)
+ {
if (featureSettings == null)
{
return;
FeatureRenderer fr = getAlignPanel().getSeqPanel().seqCanvas
.getFeatureRenderer();
+ List<String> origRenderOrder = new ArrayList<>(),
+ origGroups = new ArrayList<>();
+ // preserve original render order - allows differentiation between user configured colours and autogenerated ones
+ origRenderOrder.addAll(fr.getRenderOrder());
+ origGroups.addAll(fr.getFeatureGroups());
+
fr.findAllFeatures(true);
List<String> renderOrder = fr.getRenderOrder();
FeaturesDisplayedI displayed = fr.getFeaturesDisplayed();
+ if (!mergeOnly)
+ {
+ // only clear displayed features if we are merging
displayed.clear();
+ }
// TODO this clears displayed.featuresRegistered - do we care?
-
+ //
+ // JAL-3330 - JBP - yes we do - calling applyFeatureStyle to a view where
+ // feature visibility has already been configured is not very friendly
/*
* set feature colour if specified by feature settings
* set visibility of all features
{
FeatureColourI preferredColour = featureSettings
.getFeatureColour(type);
+ FeatureColourI origColour = fr.getFeatureStyle(type);
+ if (!mergeOnly || (!origRenderOrder.contains(type)
+ || origColour == null
+ || (!origColour.isGraduatedColour()
+ && origColour.getColour() != null
+ && origColour.getColour().equals(
+ ColorUtils.createColourFromName(type)))))
+ {
+ // if we are merging, only update if there wasn't already a colour defined for
+ // this type
if (preferredColour != null)
{
fr.setColour(type, preferredColour);
displayed.setVisible(type);
}
}
+ }
/*
* set visibility of feature groups
*/
for (String group : fr.getFeatureGroups())
{
- fr.setGroupVisibility(group, featureSettings.isGroupDisplayed(group));
+ if (!mergeOnly || !origGroups.contains(group))
+ {
+ // when merging, display groups only if the aren't already marked as not visible
+ fr.setGroupVisibility(group,
+ featureSettings.isGroupDisplayed(group));
+ }
}
/*
import jalview.io.JalviewFileView;
import jalview.util.MessageManager;
-import java.awt.BorderLayout;
import java.awt.Color;
-import java.awt.FlowLayout;
+import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileWriter;
import java.io.PrintWriter;
-import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
+import javax.swing.JCheckBox;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
private boolean wholeView;
+ /*
+ * option to export linked (CDS/peptide) features when shown
+ * on the alignment, converted to this alignment's coordinates
+ */
+ private JCheckBox includeLinkedFeatures;
+
+ /*
+ * output format option shown for feature export
+ */
+ JRadioButton GFFFormat = new JRadioButton();
+
+ /*
+ * output format option shown for annotation export
+ */
+ JRadioButton CSVFormat = new JRadioButton();
+
+ private JPanel linkedFeaturesPanel;
+
+ /**
+ * Constructor
+ *
+ * @param panel
+ */
public AnnotationExporter(AlignmentPanel panel)
{
this.ap = panel;
frame = new JInternalFrame();
frame.setContentPane(this);
frame.setLayer(JLayeredPane.PALETTE_LAYER);
- Desktop.addInternalFrame(frame, "", frame.getPreferredSize().width,
- frame.getPreferredSize().height);
+ Dimension preferredSize = frame.getPreferredSize();
+ Desktop.addInternalFrame(frame, "", true, preferredSize.width,
+ preferredSize.height, true, true);
}
/**
- * Configures the diglog for options to export visible features
+ * Configures the dialog for options to export visible features. If from a split
+ * frame panel showing linked features, make the option to include these in the
+ * export visible.
*/
public void exportFeatures()
{
exportFeatures = true;
CSVFormat.setVisible(false);
+ if (ap.av.isShowComplementFeatures())
+ {
+ linkedFeaturesPanel.setVisible(true);
+ frame.pack();
+ }
frame.setTitle(MessageManager.getString("label.export_features"));
}
FeaturesFile formatter = new FeaturesFile();
final FeatureRenderer fr = ap.getFeatureRenderer();
+ boolean includeComplement = includeLinkedFeatures.isSelected();
+
if (GFFFormat.isSelected())
{
- text = formatter.printGffFormat(sequences, fr, includeNonPositional);
+ text = formatter.printGffFormat(sequences, fr, includeNonPositional,
+ includeComplement);
}
else
{
text = formatter.printJalviewFormat(sequences, fr,
- includeNonPositional);
+ includeNonPositional, includeComplement);
}
return text;
}
}
}
+ /**
+ * Adds widgets to the panel
+ *
+ * @throws Exception
+ */
private void jbInit() throws Exception
{
- this.setLayout(new BorderLayout());
+ this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ this.setBackground(Color.white);
+
+ JPanel formatPanel = buildFormatOptionsPanel();
+ JPanel linkedFeatures = buildLinkedFeaturesPanel();
+ JPanel actionsPanel = buildActionsPanel();
+
+ this.add(formatPanel);
+ this.add(linkedFeatures);
+ this.add(actionsPanel);
+ }
+
+ /**
+ * Builds a panel with a checkbox for the option to export linked (CDS/peptide)
+ * features. This is hidden by default, and only made visible if exporting
+ * features from a split frame panel which is configured to show linked
+ * features.
+ *
+ * @return
+ */
+ private JPanel buildLinkedFeaturesPanel()
+ {
+ linkedFeaturesPanel = new JPanel();
+ linkedFeaturesPanel.setOpaque(false);
+
+ boolean nucleotide = ap.av.isNucleotide();
+ String complement = nucleotide
+ ? MessageManager.getString("label.protein").toLowerCase()
+ : "CDS";
+ JLabel label = new JLabel(
+ MessageManager.formatMessage("label.include_linked_features",
+ complement));
+ label.setHorizontalAlignment(SwingConstants.TRAILING);
+ String tooltip = MessageManager
+ .formatMessage("label.include_linked_tooltip", complement);
+ label.setToolTipText(
+ JvSwingUtils.wrapTooltip(true, tooltip));
+
+ includeLinkedFeatures = new JCheckBox();
+ linkedFeaturesPanel.add(label);
+ linkedFeaturesPanel.add(includeLinkedFeatures);
+ linkedFeaturesPanel.setVisible(false);
+
+ return linkedFeaturesPanel;
+ }
+
+ /**
+ * Builds the panel with to File or Textbox or Close actions
+ *
+ * @return
+ */
+ JPanel buildActionsPanel()
+ {
+ JPanel actionsPanel = new JPanel();
+ actionsPanel.setOpaque(false);
- toFile.setText(MessageManager.getString("label.to_file"));
+ JButton toFile = new JButton(MessageManager.getString("label.to_file"));
toFile.addActionListener(new ActionListener()
{
@Override
toFile_actionPerformed();
}
});
- toTextbox.setText(MessageManager.getString("label.to_textbox"));
+ JButton toTextbox = new JButton(
+ MessageManager.getString("label.to_textbox"));
toTextbox.addActionListener(new ActionListener()
{
@Override
toTextbox_actionPerformed();
}
});
- close.setText(MessageManager.getString("action.close"));
+ JButton close = new JButton(MessageManager.getString("action.close"));
close.addActionListener(new ActionListener()
{
@Override
close_actionPerformed();
}
});
+
+ actionsPanel.add(toFile);
+ actionsPanel.add(toTextbox);
+ actionsPanel.add(close);
+
+ return actionsPanel;
+ }
+
+ /**
+ * Builds the panel with options to output in Jalview, GFF or CSV format. GFF is
+ * only made visible when exporting features, CSV only when exporting
+ * annotation.
+ *
+ * @return
+ */
+ JPanel buildFormatOptionsPanel()
+ {
+ JPanel formatPanel = new JPanel();
+ // formatPanel.setBorder(BorderFactory.createEtchedBorder());
+ formatPanel.setOpaque(false);
+
+ JRadioButton jalviewFormat = new JRadioButton("Jalview");
jalviewFormat.setOpaque(false);
jalviewFormat.setSelected(true);
- jalviewFormat.setText("Jalview");
GFFFormat.setOpaque(false);
GFFFormat.setText("GFF");
CSVFormat.setOpaque(false);
CSVFormat.setText(MessageManager.getString("label.csv_spreadsheet"));
- jLabel1.setHorizontalAlignment(SwingConstants.TRAILING);
- jLabel1.setText(MessageManager.getString("action.format") + " ");
- this.setBackground(Color.white);
- jPanel3.setBorder(BorderFactory.createEtchedBorder());
- jPanel3.setOpaque(false);
- jPanel1.setOpaque(false);
- jPanel1.add(toFile);
- jPanel1.add(toTextbox);
- jPanel1.add(close);
- jPanel3.add(jLabel1);
- jPanel3.add(jalviewFormat);
- jPanel3.add(GFFFormat);
- jPanel3.add(CSVFormat);
+
+ ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(jalviewFormat);
buttonGroup.add(GFFFormat);
buttonGroup.add(CSVFormat);
- this.add(jPanel3, BorderLayout.CENTER);
- this.add(jPanel1, BorderLayout.SOUTH);
- }
-
- JPanel jPanel1 = new JPanel();
-
- JButton toFile = new JButton();
-
- JButton toTextbox = new JButton();
-
- JButton close = new JButton();
- ButtonGroup buttonGroup = new ButtonGroup();
+ JLabel format = new JLabel(
+ MessageManager.getString("action.format") + " ");
+ format.setHorizontalAlignment(SwingConstants.TRAILING);
- JRadioButton jalviewFormat = new JRadioButton();
+ formatPanel.add(format);
+ formatPanel.add(jalviewFormat);
+ formatPanel.add(GFFFormat);
+ formatPanel.add(CSVFormat);
- JRadioButton GFFFormat = new JRadioButton();
-
- JRadioButton CSVFormat = new JRadioButton();
-
- JLabel jLabel1 = new JLabel();
-
- JPanel jPanel3 = new JPanel();
-
- FlowLayout flowLayout1 = new FlowLayout();
+ return formatPanel;
+ }
}
seq.getLength());
if (geneLoci != null)
{
- MapList map = geneLoci.getMap();
+ MapList map = geneLoci.getMapping();
int mappedFromLength = MappingUtils.getLength(map.getFromRanges());
if (mappedFromLength == seq.getLength())
{
seq.setGeneLoci(geneLoci.getSpeciesId(), geneLoci.getAssemblyId(),
- geneLoci.getChromosomeId(), geneLoci.getMap());
+ geneLoci.getChromosomeId(), geneLoci.getMapping());
retrievedLoci.put(dbref, geneLoci);
return true;
}
seq.getLength());
if (geneLoci != null)
{
- MapList map = geneLoci.getMap();
+ MapList map = geneLoci.getMapping();
int mappedFromLength = MappingUtils.getLength(map.getFromRanges());
if (mappedFromLength == seq.getLength())
{
seq.setGeneLoci(geneLoci.getSpeciesId(), geneLoci.getAssemblyId(),
- geneLoci.getChromosomeId(), geneLoci.getMap());
+ geneLoci.getChromosomeId(), geneLoci.getMapping());
retrievedLoci.put(dbref, geneLoci);
return true;
}
import jalview.io.IdentifyFile;
import jalview.io.JalviewFileChooser;
import jalview.io.JalviewFileView;
-import jalview.jbgui.GDesktop;
import jalview.jbgui.GSplitFrame;
import jalview.jbgui.GStructureViewer;
import jalview.project.Jalview2XML;
import jalview.util.ImageMaker.TYPE;
import jalview.util.MessageManager;
import jalview.util.Platform;
+import jalview.util.ShortcutKeyMaskExWrapper;
import jalview.util.UrlConstants;
import jalview.viewmodel.AlignmentViewport;
-import jalview.ws.jws1.Discoverer;
import jalview.ws.params.ParamManager;
import jalview.ws.utils.UrlDownloadClient;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
-import java.io.BufferedInputStream;
import java.io.File;
-import java.io.FileOutputStream;
+import java.io.FileWriter;
import java.io.IOException;
+import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.ListIterator;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
-import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.event.HyperlinkEvent.EventType;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
-import javax.swing.event.MenuEvent;
-import javax.swing.event.MenuListener;
import org.stackoverflowusers.file.WindowsShortcut;
* @author $author$
* @version $Revision: 1.155 $
*/
-@SuppressWarnings("serial")
-public class Desktop extends GDesktop
+public class Desktop extends jalview.jbgui.GDesktop
implements DropTargetListener, ClipboardOwner, IProgressIndicator,
StructureSelectionManagerProvider, ApplicationSingletonI
{
+ private static final String CITATION = "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
+ + "<br><br>For help, see the FAQ at <a href=\"http://www.jalview.org/faq\">www.jalview.org/faq</a> and/or join the jalview-discuss@jalview.org mailing list"
+ + "<br><br>If you use Jalview, please cite:"
+ + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
+ + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
+ + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033";
+
+ private static final String DEFAULT_AUTHORS = "The Jalview Authors (See AUTHORS file for current list)";
private final static int DEFAULT_MIN_WIDTH = 300;
private final static String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
+ protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
+
+ public static HashMap<String, FileWriter> savingFiles = new HashMap<>();
+
private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
/**
* news reader - null if it was never started.
*/
- BlogReader jvnews = null;
+ private BlogReader jvnews = null;
private File projectFile;
listener);
}
- public static MyDesktopPane getDesktopPane()
- {
- Desktop desktop = Desktop.getInstance();
- return desktop == null ? null : desktop.desktopPane;
- }
-
public static StructureSelectionManager getStructureSelectionManager()
{
return StructureSelectionManager
static final int yOffset = 30;
- public Discoverer discoverer;
+ // BH was static
+ public jalview.ws.jws1.Discoverer discoverer;
+ //BH was static
public Object[] jalviewClipboard;
+//BH was static
public boolean internalCopy = false;
private static int fileLoadingCount = 0;
// All other methods, simply delegate
}
-
- public MyDesktopPane desktopPane;
-
- /**
- * Answers an 'application scope' singleton instance of this class. Separate
- * SwingJS 'applets' running in the same browser page will each have a
- * distinct instance of Desktop.
- *
- * @return
- */
- public static Desktop getInstance()
- {
- return Jalview.isHeadlessMode() ? null
- : (Desktop) ApplicationSingletonProvider
- .getInstance(Desktop.class);
- }
-
/**
* Private constructor enforces singleton pattern. It is called by reflection
* from ApplicationSingletonProvider.getInstance().
* block are spawned off as threads rather than waited for during this
* constructor.
*/
- if (!Platform.isJS())
+
+ doConfigureStructurePrefs();
+ setTitle("Jalview " + Cache.getProperty("VERSION"));
+
+ try
+ {
+ if (Platform.getJavaVersion() >= 11)
+ {
+ // BH use reflection so that this code can be in both the Java8 and
+ // Java11 versions
+ Class<?> j11APQHandlers = Class
+ .forName("jalview.gui.APQHandlers");
+ Method meth = j11APQHandlers.getMethod("setAPQHandlers",
+ new Class<?>[]
+ { Desktop.class });
+ meth.invoke(j11APQHandlers.newInstance(), this);
+ }
+ } catch (Throwable t)
{
- doVamsasClientCheck();
+ System.out.println(
+ "Desktop Error setting APQHandlers: " + t.toString());
}
- doConfigureStructurePrefs();
- setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
- setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
+ addWindowListener(new WindowAdapter()
+ {
+
+ @Override
+ public void windowClosing(WindowEvent ev)
+ {
+ quit();
+ }
+ });
+
+ boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE",
false);
- boolean showjconsole = jalview.bin.Cache
- .getDefault("SHOW_JAVA_CONSOLE", false);
- desktopPane = new MyDesktopPane(selmemusage);
- showMemusage.setSelected(selmemusage);
- desktopPane.setBackground(Color.white);
+ boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE",
+ false);
+ desktopPane = new MyDesktopPane(selmemusage);
+
+ showMemusage.setSelected(selmemusage);
+ desktopPane.setBackground(Color.white);
+
getContentPane().setLayout(new BorderLayout());
// alternate config - have scrollbars - see notes in JAL-153
// JScrollPane sp = new JScrollPane();
{
jconsole = new Console(this, showjconsole);
- // add essential build information
- jconsole.setHeader("Jalview Version: "
- + jalview.bin.Cache.getProperty("VERSION") + "\n"
- + "Jalview Installation: "
- + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
- + "\n" + "Build Date: "
- + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
- + "\n" + "Java version: "
- + System.getProperty("java.version") + "\n"
- + System.getProperty("os.arch") + " "
- + System.getProperty("os.name") + " "
- + System.getProperty("os.version"));
-
+ jconsole.setHeader(Cache.getVersionDetailsForConsole());
showConsole(showjconsole);
showNews.setVisible(false);
@Override
public void run()
{
- new SplashScreen();
+ new SplashScreen(true);
}
});
// configure services
StructureSelectionManager ssm = StructureSelectionManager
.getStructureSelectionManager(this);
- if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
+ if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
{
- ssm.setAddTempFacAnnot(jalview.bin.Cache
+ ssm.setAddTempFacAnnot(Cache
.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
- ssm.setProcessSecondaryStructure(jalview.bin.Cache
+ ssm.setProcessSecondaryStructure(Cache
.getDefault(Preferences.STRUCT_FROM_PDB, true));
ssm.setSecStructServices(
- jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
+ Cache.getDefault(Preferences.USE_RNAVIEW, true));
}
else
{
public void run()
{
Cache.log.debug("Downloading data from identifiers.org");
- // UrlDownloadClient client = new UrlDownloadClient();
try
{
UrlDownloadClient.download(IdOrgSettings.getUrl(),
{
// TODO: lock aspect ratio for scaling desktop Bug #0058199
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
- String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
- String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
- String width = jalview.bin.Cache
+ String x = Cache.getProperty(windowName + "SCREEN_X");
+ String y = Cache.getProperty(windowName + "SCREEN_Y");
+ String width = Cache
.getProperty(windowName + "SCREEN_WIDTH");
- String height = jalview.bin.Cache
+ String height = Cache
.getProperty(windowName + "SCREEN_HEIGHT");
if ((x != null) && (y != null) && (width != null) && (height != null))
{
int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
iw = Integer.parseInt(width), ih = Integer.parseInt(height);
- if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
+ if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
{
// attempt #1 - try to cope with change in screen geometry - this
// version doesn't preserve original jv aspect ratio.
// take ratio of current screen size vs original screen size.
double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
- jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
+ Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
- jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
+ Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
// rescale the bounds depending upon the current screen geometry.
ix = (int) (ix * sw);
iw = (int) (iw * sw);
ih = (int) (ih * sh);
while (ix >= screenSize.width)
{
- jalview.bin.Cache.log.debug(
+ Cache.log.debug(
"Window geometry location recall error: shifting horizontal to within screenbounds.");
ix -= screenSize.width;
}
while (iy >= screenSize.height)
{
- jalview.bin.Cache.log.debug(
+ Cache.log.debug(
"Window geometry location recall error: shifting vertical to within screenbounds.");
iy -= screenSize.height;
}
- jalview.bin.Cache.log.debug(
+ Cache.log.debug(
"Got last known dimensions for " + windowName + ": x:" + ix
+ " y:" + iy + " width:" + iw + " height:" + ih);
}
return null;
}
- private void doVamsasClientCheck()
- {
- if (Cache.vamsasJarsPresent())
- {
- setupVamsasDisconnectedGui();
- VamsasMenu.setVisible(true);
- final Desktop us = this;
- VamsasMenu.addMenuListener(new MenuListener()
- {
- // this listener remembers when the menu was first selected, and
- // doesn't rebuild the session list until it has been cleared and
- // reselected again.
- boolean refresh = true;
-
- @Override
- public void menuCanceled(MenuEvent e)
- {
- refresh = true;
- }
-
- @Override
- public void menuDeselected(MenuEvent e)
- {
- refresh = true;
- }
-
- @Override
- public void menuSelected(MenuEvent e)
- {
- if (refresh)
- {
- us.buildVamsasStMenu();
- refresh = false;
- }
- }
- });
- vamsasStart.setVisible(true);
- }
- }
-
protected void showPasteMenu(int x, int y)
{
JPopupMenu popup = new JPopupMenu();
FileFormatI format = new IdentifyFile().identify(file,
DataSourceType.PASTE);
- new FileLoader().loadFile(file, DataSourceType.PASTE, format);
+ new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
}
} catch (Exception ex)
// the current window title
frame.setTitle(title);
+ // BH fix
if (w > 0 && (frame.getWidth() < 1 || frame.getHeight() < 1))
{
frame.setSize(w, h);
*/
private static void setKeyBindings(JInternalFrame frame)
{
+ @SuppressWarnings("serial")
final Action closeAction = new AbstractAction()
{
@Override
KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
InputEvent.CTRL_DOWN_MASK);
KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
- Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
+ ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
InputMap inputMap = frame
.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
if (file instanceof File)
{
Platform.cacheFileData((File) file);
+ new FileLoader().loadFile(null, (File) file, protocol, format);
}
- new FileLoader().loadFile(null, file, protocol, format);
+ else
+ {
+ new FileLoader().LoadFile((String) file, protocol, format);
+ }
}
} catch (Exception ex)
{
{
String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
JalviewFileChooser chooser = JalviewFileChooser
- .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
+ .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, BackupFiles.getEnabled());
chooser.setFileView(new JalviewFileView());
chooser.setDialogTitle(
}
}
- new FileLoader().loadFile(viewport, selectedFile,
+ new FileLoader().LoadFile(viewport, selectedFile,
DataSourceType.FILE, format);
}
});
{
if (viewport != null)
{
- new FileLoader().loadFile(viewport, url, DataSourceType.URL,
+ new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
FileFormat.Jalview);
}
else
{
- new FileLoader().loadFile(url, DataSourceType.URL,
+ new FileLoader().LoadFile(url, DataSourceType.URL,
FileFormat.Jalview);
}
}
if (viewport != null)
{
- new FileLoader().loadFile(viewport, url, DataSourceType.URL,
+ new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
format);
}
else
{
- new FileLoader().loadFile(url, DataSourceType.URL, format);
+ new FileLoader().LoadFile(url, DataSourceType.URL, format);
}
}
}
public void quit()
{
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
- jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
+ Cache.setProperty("SCREENGEOMETRY_WIDTH",
screen.width + "");
- jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
+ Cache.setProperty("SCREENGEOMETRY_HEIGHT",
screen.height + "");
storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
getWidth(), getHeight()));
private void storeLastKnownDimensions(String string, Rectangle jc)
{
- jalview.bin.Cache.log.debug("Storing last known dimensions for "
+ Cache.log.debug("Storing last known dimensions for "
+ string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
+ " height:" + jc.height);
- jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
- jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
- jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
- jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
+ Cache.setProperty(string + "SCREEN_X", jc.x + "");
+ Cache.setProperty(string + "SCREEN_Y", jc.y + "");
+ Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
+ Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
}
/**
@Override
public void aboutMenuItem_actionPerformed(ActionEvent e)
{
- // StringBuffer message = getAboutMessage(false);
- // JvOptionPane.showInternalMessageDialog(Desktop.getDesktop(),
- //
- // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
new Thread(new Runnable()
{
@Override
public void run()
{
+ // BH! true meaning "interactive" here (applet branch); was false in
+ // develop version??
new SplashScreen(true);
}
}).start();
}
- public StringBuffer getAboutMessage(boolean shortv)
+ /**
+ * Returns the html text for the About screen, including any available version
+ * number, build details, author details and citation reference, but without
+ * the enclosing {@code html} tags
+ *
+ * @return
+ */
+ public String getAboutMessage()
{
- StringBuffer message = new StringBuffer();
- message.append("<html>");
- if (shortv)
- {
- message.append("<h1><strong>Version: "
- + jalview.bin.Cache.getProperty("VERSION")
- + "</strong></h1>");
- message.append("<strong>Last Updated: <em>"
- + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
- + "</em></strong>");
+ StringBuilder message = new StringBuilder(1024);
+ message.append("<h1><strong>Version: ")
+ .append(Cache.getProperty("VERSION")).append("</strong></h1>")
+ .append("<strong>Built: <em>")
+ .append(Cache.getDefault("BUILD_DATE", "unknown"))
+ .append("</em> from ").append(Cache.getBuildDetailsForSplash())
+ .append("</strong>");
- }
- else
+ String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
+ if (latestVersion.equals("Checking"))
{
-
- message.append("<strong>Version "
- + jalview.bin.Cache.getProperty("VERSION")
- + "; last updated: "
- + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
+ // JBP removed this message for 2.11: May be reinstated in future version
+ // message.append("<br>...Checking latest version...</br>");
}
-
- if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
- .equals("Checking"))
- {
- message.append("<br>...Checking latest version...</br>");
- }
- else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
- .equals(jalview.bin.Cache.getProperty("VERSION")))
+ else if (!latestVersion.equals(Cache.getProperty("VERSION")))
{
boolean red = false;
- if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
+ if (Cache.getProperty("VERSION").toLowerCase()
.indexOf("automated build") == -1)
{
red = true;
message.append("<div style=\"color: #FF0000;font-style: bold;\">");
}
- message.append("<br>!! Version "
- + jalview.bin.Cache.getDefault("LATEST_VERSION",
- "..Checking..")
- + " is available for download from "
- + jalview.bin.Cache.getDefault("www.jalview.org",
- "http://www.jalview.org")
- + " !!");
+ message.append("<br>!! Version ")
+ .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
+ .append(" is available for download from ")
+ .append(Cache.getDefault("www.jalview.org",
+ "http://www.jalview.org"))
+ .append(" !!");
if (red)
{
message.append("</div>");
}
}
- message.append("<br>Authors: " + jalview.bin.Cache.getDefault(
- "AUTHORFNAMES",
- "The Jalview Authors (See AUTHORS file for current list)")
- + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
- + "<br><br>For help, see the FAQ at <a href=\"http://www.jalview.org/faq\">www.jalview.org/faq</a> and/or join the jalview-discuss@jalview.org mailing list"
- + "<br><br>If you use Jalview, please cite:"
- + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
- + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
- + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
- + "</html>");
- return message;
+ message.append("<br>Authors: ");
+ message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
+ message.append(CITATION);
+
+ return message.toString();
}
/**
}
Jalview.setCurrentAlignFrame(null);
System.out.println("ALL CLOSED");
- if (v_client != null)
- {
- // TODO clear binding to vamsas document objects on close_all
- }
/*
* reset state of singleton objects as appropriate (clear down session state
protected void garbageCollect_actionPerformed(ActionEvent e)
{
// We simply collect the garbage
- jalview.bin.Cache.log.debug("Collecting garbage...");
+ Cache.log.debug("Collecting garbage...");
System.gc();
- jalview.bin.Cache.log.debug("Finished garbage collection.");
+ Cache.log.debug("Finished garbage collection.");
}
/*
setProgressBar(MessageManager.formatMessage(
"label.saving_jalview_project", new Object[]
{ chosenFile.getName() }), chosenFile.hashCode());
- jalview.bin.Cache.setProperty("LAST_DIRECTORY",
+ Cache.setProperty("LAST_DIRECTORY",
chosenFile.getParent());
// TODO catch and handle errors for savestate
// TODO prevent user from messing with the Desktop whilst we're saving
try
{
- boolean doBackup = BackupFiles.getEnabled();
+ boolean doBackup = BackupFiles.getEnabled();
BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
"Jalview Project (old)" };
JalviewFileChooser chooser = new JalviewFileChooser(
Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
- "Jalview Project", true, true); // last two booleans: allFiles,
+ "Jalview Project", true, BackupFiles.getEnabled()); // last two booleans: allFiles,
// allowBackupFiles
chooser.setFileView(new JalviewFileView());
chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
@Override
public void run()
{
- try
+ try
{
+ // BH was String "choice" here but needs to be File object
new Jalview2XML().loadJalviewAlign(selectedFile);
} catch (OutOfMemoryError oom)
- {
- new OOMWarning("Whilst loading project from " + choice, oom);
- } catch (Exception ex)
- {
- Cache.log.error(
- "Problems whilst loading project from " + choice, ex);
+ {
+ new OOMWarning("Whilst loading project from " + choice, oom);
+ } catch (Exception ex)
+ {
+ Cache.log.error(
+ "Problems whilst loading project from " + choice, ex);
JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
- MessageManager.formatMessage(
- "label.error_whilst_loading_project_from",
- new Object[]
- { choice }),
- MessageManager.getString("label.couldnt_load_project"),
- JvOptionPane.WARNING_MESSAGE);
- }
+ MessageManager.formatMessage(
+ "label.error_whilst_loading_project_from",
+ new Object[]
+ { choice }),
+ MessageManager
+ .getString("label.couldnt_load_project"),
+ JvOptionPane.WARNING_MESSAGE);
+ }
}
}).start();
}
return;
}
+ // BH! not in applet branch
+ // FIXME: ideally should use UI interface API
+ FeatureSettings viewFeatureSettings = (af.featureSettings != null
+ && af.featureSettings.isOpen())
+ ? af.featureSettings
+ : null;
+ Rectangle fsBounds = af.getFeatureSettingsGeometry();
for (int i = 0; i < size; i++)
{
AlignmentPanel ap = af.alignPanels.get(i);
AlignFrame newaf = new AlignFrame(ap);
+ // BH! not in applet branch
+ // transfer reference for existing feature settings to new alignFrame
+ if (ap == af.alignPanel)
+ {
+ if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
+ {
+ newaf.featureSettings = viewFeatureSettings;
+ }
+ newaf.setFeatureSettingsGeometry(fsBounds);
+ }
+
/*
* Restore the view's last exploded frame geometry if known. Multiple
* views from one exploded frame share and restore the same (frame)
addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
AlignFrame.DEFAULT_HEIGHT);
+ // BH! not in applet branch
+ // and materialise a new feature settings dialog instance for the new alignframe
+ // (closes the old as if 'OK' was pressed)
+ if (ap == af.alignPanel && newaf.featureSettings != null
+ && newaf.featureSettings.isOpen()
+ && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
+ {
+ newaf.showFeatureSettingsUI();
+ }
}
+ // BH! not in applet branch
+ af.featureSettings = null;
af.alignPanels.clear();
af.closeMenuItem_actionPerformed(true);
if (gatherThis)
{
- af.alignPanels.clear();
- af.closeMenuItem_actionPerformed(true);
- }
- }
- }
-
- }
-
- jalview.gui.VamsasApplication v_client = null;
-
- @Override
- public void vamsasImport_actionPerformed(ActionEvent e)
- {
- // TODO: JAL-3048 not needed for Jalview-JS
-
- if (v_client == null)
- {
- // Load and try to start a session.
- JalviewFileChooser chooser = new JalviewFileChooser(
- jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
-
- chooser.setFileView(new JalviewFileView());
- chooser.setDialogTitle(
- MessageManager.getString("label.open_saved_vamsas_session"));
- chooser.setToolTipText(MessageManager.getString(
- "label.select_vamsas_session_opened_as_new_vamsas_session"));
-
- int value = chooser.showOpenDialog(this);
-
- if (value == JalviewFileChooser.APPROVE_OPTION)
- {
- String fle = chooser.getSelectedFile().toString();
- if (!vamsasImport(chooser.getSelectedFile()))
- {
- JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
- MessageManager.formatMessage(
- "label.couldnt_import_as_vamsas_session",
- new Object[]
- { fle }),
- MessageManager
- .getString("label.vamsas_document_import_failed"),
- JvOptionPane.ERROR_MESSAGE);
- }
- }
- }
- else
- {
- jalview.bin.Cache.log.error(
- "Implementation error - load session from a running session is not supported.");
- }
- }
-
- /**
- * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
- *
- * @param file
- * @return true if import was a success and a session was started.
- */
- public boolean vamsasImport(URL url)
- {
- // TODO: create progress bar
- if (v_client != null)
- {
-
- jalview.bin.Cache.log.error(
- "Implementation error - load session from a running session is not supported.");
- return false;
- }
-
- try
- {
- // copy the URL content to a temporary local file
- // TODO: be a bit cleverer here with nio (?!)
- File file = File.createTempFile("vdocfromurl", ".vdj");
- FileOutputStream fos = new FileOutputStream(file);
- BufferedInputStream bis = new BufferedInputStream(url.openStream());
- byte[] buffer = new byte[2048];
- int ln;
- while ((ln = bis.read(buffer)) > -1)
- {
- fos.write(buffer, 0, ln);
- }
- bis.close();
- fos.close();
- v_client = new jalview.gui.VamsasApplication(this, file,
- url.toExternalForm());
- } catch (Exception ex)
- {
- jalview.bin.Cache.log.error(
- "Failed to create new vamsas session from contents of URL "
- + url,
- ex);
- return false;
- }
- setupVamsasConnectedGui();
- v_client.initial_update(); // TODO: thread ?
- return v_client.inSession();
- }
-
- /**
- * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
- *
- * @param file
- * @return true if import was a success and a session was started.
- */
- public boolean vamsasImport(File file)
- {
- if (v_client != null)
- {
-
- jalview.bin.Cache.log.error(
- "Implementation error - load session from a running session is not supported.");
- return false;
- }
-
- setProgressBar(MessageManager.formatMessage(
- "status.importing_vamsas_session_from", new Object[]
- { file.getName() }), file.hashCode());
- try
- {
- v_client = new jalview.gui.VamsasApplication(this, file, null);
- } catch (Exception ex)
- {
- setProgressBar(MessageManager.formatMessage(
- "status.importing_vamsas_session_from", new Object[]
- { file.getName() }), file.hashCode());
- jalview.bin.Cache.log.error(
- "New vamsas session from existing session file failed:", ex);
- return false;
- }
- setupVamsasConnectedGui();
- v_client.initial_update(); // TODO: thread ?
- setProgressBar(MessageManager.formatMessage(
- "status.importing_vamsas_session_from", new Object[]
- { file.getName() }), file.hashCode());
- return v_client.inSession();
- }
-
- public boolean joinVamsasSession(String mysesid)
- {
- if (v_client != null)
- {
- throw new Error(MessageManager
- .getString("error.try_join_vamsas_session_another"));
- }
- if (mysesid == null)
- {
- throw new Error(
- MessageManager.getString("error.invalid_vamsas_session_id"));
- }
- v_client = new VamsasApplication(this, mysesid);
- setupVamsasConnectedGui();
- v_client.initial_update();
- return (v_client.inSession());
- }
-
- @Override
- public void vamsasStart_actionPerformed(ActionEvent e)
- {
- if (v_client == null)
- {
- // Start a session.
- // we just start a default session for moment.
- /*
- * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
- * getProperty("LAST_DIRECTORY"));
- *
- * chooser.setFileView(new JalviewFileView());
- * chooser.setDialogTitle("Load Vamsas file");
- * chooser.setToolTipText("Import");
- *
- * int value = chooser.showOpenDialog(this);
- *
- * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
- * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
- */
- v_client = new VamsasApplication(this);
- setupVamsasConnectedGui();
- v_client.initial_update(); // TODO: thread ?
- }
- else
- {
- // store current data in session.
- v_client.push_update(); // TODO: thread
- }
- }
-
- protected void setupVamsasConnectedGui()
- {
- vamsasStart.setText(MessageManager.getString("label.session_update"));
- vamsasSave.setVisible(true);
- vamsasStop.setVisible(true);
- vamsasImport.setVisible(false); // Document import to existing session is
- // not possible for vamsas-client-1.0.
- }
-
- protected void setupVamsasDisconnectedGui()
- {
- vamsasSave.setVisible(false);
- vamsasStop.setVisible(false);
- vamsasImport.setVisible(true);
- vamsasStart
- .setText(MessageManager.getString("label.new_vamsas_session"));
- }
-
- @Override
- public void vamsasStop_actionPerformed(ActionEvent e)
- {
- if (v_client != null)
- {
- v_client.end_session();
- v_client = null;
- setupVamsasDisconnectedGui();
- }
- }
-
- protected void buildVamsasStMenu()
- {
- if (v_client == null)
- {
- String[] sess = null;
- try
- {
- sess = VamsasApplication.getSessionList();
- } catch (Exception e)
- {
- jalview.bin.Cache.log.warn("Problem getting current sessions list.",
- e);
- sess = null;
- }
- if (sess != null)
- {
- jalview.bin.Cache.log.debug(
- "Got current sessions list: " + sess.length + " entries.");
- VamsasStMenu.removeAll();
- for (int i = 0; i < sess.length; i++)
- {
- JMenuItem sessit = new JMenuItem();
- sessit.setText(sess[i]);
- sessit.setToolTipText(MessageManager
- .formatMessage("label.connect_to_session", new Object[]
- { sess[i] }));
- final Desktop dsktp = this;
- final String mysesid = sess[i];
- sessit.addActionListener(new ActionListener()
- {
-
- @Override
- public void actionPerformed(ActionEvent e)
+ if (af.featureSettings != null && af.featureSettings.isOpen())
{
- if (dsktp.v_client == null)
+ if (source.featureSettings == null)
{
- Thread rthr = new Thread(new Runnable()
- {
-
- @Override
- public void run()
- {
- dsktp.v_client = new VamsasApplication(dsktp, mysesid);
- dsktp.setupVamsasConnectedGui();
- dsktp.v_client.initial_update();
- }
-
- });
- rthr.start();
+ // preserve the feature settings geometry for this frame
+ source.featureSettings = af.featureSettings;
+ source.setFeatureSettingsGeometry(
+ af.getFeatureSettingsGeometry());
+ }
+ else
+ {
+ // close it and forget
+ af.featureSettings.close();
}
}
- });
- VamsasStMenu.add(sessit);
- }
- // don't show an empty menu.
- VamsasStMenu.setVisible(sess.length > 0);
-
- }
- else
- {
- jalview.bin.Cache.log.debug("No current vamsas sessions.");
- VamsasStMenu.removeAll();
- VamsasStMenu.setVisible(false);
- }
- }
- else
- {
- // Not interested in the content. Just hide ourselves.
- VamsasStMenu.setVisible(false);
- }
- }
-
- @Override
- public void vamsasSave_actionPerformed(ActionEvent e)
- {
- // TODO: JAL-3048 not needed for Jalview-JS
-
- if (v_client != null)
- {
- // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
- JalviewFileChooser chooser = new JalviewFileChooser("vdj",
- "Vamsas Document");
-
- chooser.setFileView(new JalviewFileView());
- chooser.setDialogTitle(MessageManager
- .getString("label.save_vamsas_document_archive"));
-
- int value = chooser.showSaveDialog(this);
-
- if (value == JalviewFileChooser.APPROVE_OPTION)
- {
- java.io.File choice = chooser.getSelectedFile();
- JPanel progpanel = addProgressPanel(MessageManager
- .formatMessage("label.saving_vamsas_doc", new Object[]
- { choice.getName() }));
- Cache.setProperty("LAST_DIRECTORY", choice.getParent());
- String warnmsg = null;
- String warnttl = null;
- try
- {
- v_client.vclient.storeDocument(choice);
- } catch (Error ex)
- {
- warnttl = "Serious Problem saving Vamsas Document";
- warnmsg = ex.toString();
- jalview.bin.Cache.log
- .error("Error Whilst saving document to " + choice, ex);
-
- } catch (Exception ex)
- {
- warnttl = "Problem saving Vamsas Document.";
- warnmsg = ex.toString();
- jalview.bin.Cache.log.warn(
- "Exception Whilst saving document to " + choice, ex);
-
- }
- removeProgressPanel(progpanel);
- if (warnmsg != null)
- {
- JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
-
- warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
+ af.alignPanels.clear();
+ af.closeMenuItem_actionPerformed(true);
+ }
}
}
- }
- }
-
- JPanel vamUpdate = null;
-
- /**
- * hide vamsas user gui bits when a vamsas document event is being handled.
- *
- * @param b
- * true to hide gui, false to reveal gui
- */
- public void setVamsasUpdate(boolean b)
- {
- Cache.log.debug("Setting gui for Vamsas update "
- + (b ? "in progress" : "finished"));
-
- if (vamUpdate != null)
+ // refresh the feature setting UI for the source frame if it exists
+ if (source.featureSettings != null
+ && source.featureSettings.isOpen())
{
- this.removeProgressPanel(vamUpdate);
+ source.showFeatureSettingsUI();
}
- if (b)
- {
- vamUpdate = this.addProgressPanel(
- MessageManager.getString("label.updating_vamsas_session"));
- }
- vamsasStart.setVisible(!b);
- vamsasStop.setVisible(!b);
- vamsasSave.setVisible(!b);
}
public JInternalFrame[] getAllFrames()
{
if (Jalview.isHeadlessMode())
{
- // Desktop.getDesktop() is null in headless mode
+ // Desktop.getDesktopPane() is null in headless mode
return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
}
openGroovyConsole();
} catch (Exception ex)
{
- jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
+ Cache.log.error("Groovy Shell Creation failed.", ex);
JvOptionPane.showInternalMessageDialog(desktopPane,
MessageManager.getString("label.couldnt_create_groovy_shell"),
{
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
- Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
+ ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()),
"Quit");
getRootPane().getActionMap().put("Quit", new AbstractAction()
{
@Override
public void setProgressBar(String message, long id)
{
- Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
+ // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
if (progressBars == null)
{
progressBarHandlers = new Hashtable<>();
}
- if (progressBars.get(new Long(id)) != null)
+ if (progressBars.get(Long.valueOf(id)) != null)
{
- JPanel panel = progressBars.remove(new Long(id));
- if (progressBarHandlers.contains(new Long(id)))
+ JPanel panel = progressBars.remove(Long.valueOf(id));
+ if (progressBarHandlers.contains(Long.valueOf(id)))
{
- progressBarHandlers.remove(new Long(id));
+ progressBarHandlers.remove(Long.valueOf(id));
}
removeProgressPanel(panel);
}
else
{
- progressBars.put(new Long(id), addProgressPanel(message));
+ progressBars.put(Long.valueOf(id), addProgressPanel(message));
}
}
final IProgressIndicatorHandler handler)
{
if (progressBarHandlers == null
- || !progressBars.containsKey(new Long(id)))
+ || !progressBars.containsKey(Long.valueOf(id)))
{
throw new Error(MessageManager.getString(
"error.call_setprogressbar_before_registering_handler"));
}
- progressBarHandlers.put(new Long(id), handler);
- final JPanel progressPanel = progressBars.get(new Long(id));
+ progressBarHandlers.put(Long.valueOf(id), handler);
+ final JPanel progressPanel = progressBars.get(Long.valueOf(id));
if (handler.canCancel())
{
JButton cancel = new JButton(
return null;
}
- public VamsasApplication getVamsasApplication()
- {
- return v_client;
-
- }
-
/**
* flag set if jalview GUI is being operated programmatically
*/
// todo: changesupport handlers need to be transferred
if (discoverer == null)
{
- discoverer = Discoverer.getInstance();
- // register PCS handler for getDesktop().
+ discoverer = jalview.ws.jws1.Discoverer.getInstance();
+ // register PCS handler for getDesktopPane().
discoverer.addPropertyChangeListener(changeSupport);
}
// JAL-940 - disabled JWS1 service configuration - always start discoverer
{
try
{
- if (progress != null)
+ if (progress != null && !Platform.isJS())
{
progress.setProgressBar(MessageManager
.formatMessage("status.opening_params", new Object[]
{ url }), this.hashCode());
}
- BrowserLauncher.openURL(url);
+ Platform.openURL(url);
} catch (Exception ex)
{
JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
ex.printStackTrace();
}
- if (progress != null)
+ if (progress != null && !Platform.isJS())
{
progress.setProgressBar(null, this.hashCode());
}
/**
* flag indicating if dialogExecutor should try to acquire a permit
*/
- volatile boolean dialogPause = true;
+ private volatile boolean dialogPause = true;
/**
* pause the queue
*/
- java.util.concurrent.Semaphore block = new Semaphore(0);
+ private java.util.concurrent.Semaphore block = new Semaphore(0);
+ // BH was static
private groovy.ui.Console groovyConsole;
public StructureViewer lastTargetedView;
{
}
}
+ // BH! Q: do we mean System.headless ? or "nogui/nodisplay" headless?
if (Jalview.isHeadlessMode())
{
return;
return result;
}
+
+
+ public MyDesktopPane desktopPane;
+
+ /**
+ * Get the instance of the JDesktopPane from the application-local Desktop
+ * (JFrame) instance
+ *
+ * The key here is that the Java application can have multiple static
+ * instances of the desktop JFrame because those instances are sandboxed, but
+ * the SwingJS JFrames will be in the same VM-like space. So we need
+ * application singletons, at least for JavaScript.
+ *
+ * @return
+ */
+ public static MyDesktopPane getDesktopPane()
+ {
+ Desktop desktop = Desktop.getInstance();
+ return desktop == null ? null : desktop.desktopPane;
+ }
+
+ /**
+ * Answers an 'application scope' singleton instance of this class. Separate
+ * SwingJS 'applets' running in the same browser page will each have a
+ * distinct instance of Desktop.
+ *
+ * @return
+ */
+ public static Desktop getInstance()
+ {
+ return Jalview.isHeadlessMode() ? null
+ : (Desktop) ApplicationSingletonProvider
+ .getInstance(Desktop.class);
+ }
+
}
*/
package jalview.gui;
-import jalview.api.FeatureColourI;
-import jalview.api.FeatureSettingsControllerI;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SequenceI;
-import jalview.datamodel.features.FeatureMatcher;
-import jalview.datamodel.features.FeatureMatcherI;
-import jalview.datamodel.features.FeatureMatcherSet;
-import jalview.datamodel.features.FeatureMatcherSetI;
-import jalview.gui.Help.HelpId;
-import jalview.gui.JalviewColourChooser.ColourChooserListener;
-import jalview.io.JalviewFileChooser;
-import jalview.io.JalviewFileView;
-import jalview.schemes.FeatureColour;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
-import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
-import jalview.xml.binding.jalview.JalviewUserColours;
-import jalview.xml.binding.jalview.JalviewUserColours.Colour;
-import jalview.xml.binding.jalview.JalviewUserColours.Filter;
-import jalview.xml.binding.jalview.ObjectFactory;
-
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
+import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridLayout;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
+import jalview.api.AlignViewControllerGuiI;
+import jalview.api.AlignViewportI;
+import jalview.api.FeatureColourI;
+import jalview.api.FeatureSettingsControllerI;
+import jalview.api.SplitContainerI;
+import jalview.api.ViewStyleI;
+import jalview.controller.FeatureSettingsControllerGuiI;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.FeatureMatcher;
+import jalview.datamodel.features.FeatureMatcherI;
+import jalview.datamodel.features.FeatureMatcherSet;
+import jalview.datamodel.features.FeatureMatcherSetI;
+import jalview.gui.Help.HelpId;
+import jalview.gui.JalviewColourChooser.ColourChooserListener;
+import jalview.io.JalviewFileChooser;
+import jalview.io.JalviewFileView;
+import jalview.schemes.FeatureColour;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
+import jalview.viewmodel.styles.ViewStyle;
+import jalview.xml.binding.jalview.JalviewUserColours;
+import jalview.xml.binding.jalview.JalviewUserColours.Colour;
+import jalview.xml.binding.jalview.JalviewUserColours.Filter;
+import jalview.xml.binding.jalview.ObjectFactory;
+
public class FeatureSettings extends JPanel
- implements FeatureSettingsControllerI
+ implements FeatureSettingsControllerI, FeatureSettingsControllerGuiI
{
private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
.getString("label.sequence_feature_colours");
float originalTransparency;
- Map<String, FeatureMatcherSetI> originalFilters;
+ private ViewStyleI originalViewStyle;
+
+ private Map<String, FeatureMatcherSetI> originalFilters;
final JInternalFrame frame;
JSlider transparency = new JSlider();
+ private JCheckBox showComplementOnTop;
+
+ private JCheckBox showComplement;
+
/*
* when true, constructor is still executing - so ignore UI events
*/
boolean handlingUpdate = false;
/*
+ * a change listener to ensure the dialog is updated if
+ * FeatureRenderer discovers new features
+ */
+ private PropertyChangeListener change;
+
+ /*
* holds {featureCount, totalExtent} for each feature type
*/
Map<String, float[]> typeWidth = null;
+ private void storeOriginalSettings()
+ {
+ // save transparency for restore on Cancel
+ originalTransparency = fr.getTransparency();
+
+ updateTransparencySliderFromFR();
+
+ originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
+ originalViewStyle = new ViewStyle(af.viewport.getViewStyle());
+ }
+
+ private void updateTransparencySliderFromFR()
+ {
+ boolean incon = inConstruction;
+ inConstruction = true;
+
+ int transparencyAsPercent = (int) (fr.getTransparency() * 100);
+ transparency.setValue(100 - transparencyAsPercent);
+ inConstruction = incon;
+ }
/**
* Constructor
*
this.af = alignFrame;
fr = af.getFeatureRenderer();
- // save transparency for restore on Cancel
- originalTransparency = fr.getTransparency();
- int originalTransparencyAsPercent = (int) (originalTransparency * 100);
- transparency.setMaximum(100 - originalTransparencyAsPercent);
-
- originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
+ storeOriginalSettings();
try
{
@Override
public void mousePressed(MouseEvent evt)
{
- selectedRow = table.rowAtPoint(evt.getPoint());
+ Point pt = evt.getPoint();
+ selectedRow = table.rowAtPoint(pt);
String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
if (evt.isPopupTrigger())
{
Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
showPopupMenu(selectedRow, type, colour, evt.getPoint());
}
- else if (evt.getClickCount() == 2)
+ else if (evt.getClickCount() == 2
+ && table.columnAtPoint(pt) == TYPE_COLUMN)
{
boolean invertSelection = evt.isAltDown();
boolean toggleSelection = Platform.isControlDown(evt);
boolean extendSelection = evt.isShiftDown();
fr.ap.alignFrame.avc.markColumnsContainingFeatures(
invertSelection, extendSelection, toggleSelection, type);
+ fr.ap.av.sendSelection();
}
}
}
discoverAllFeatureData();
- final PropertyChangeListener change;
final FeatureSettings fs = this;
fr.addPropertyChangeListener(change = new PropertyChangeListener()
{
});
+ SplitContainerI splitframe = af.getSplitViewContainer();
+ if (splitframe != null)
+ {
+ frame = null; // keeps eclipse happy
+ splitframe.addFeatureSettingsUI(this);
+ }
+ else
+ {
frame = new JInternalFrame();
frame.setContentPane(this);
- Desktop.addInternalFrame(frame,
- MessageManager.getString("label.sequence_feature_settings"),
- 600, Platform.isAMacAndNotJS() ? 480 : 450);
+ Rectangle bounds = af.getFeatureSettingsGeometry();
+ String title;
+ if (af.getAlignPanels().size() > 1 || Desktop.getAlignmentPanels(
+ af.alignPanel.av.getSequenceSetId()).length > 1)
+ {
+ title = MessageManager.formatMessage(
+ "label.sequence_feature_settings_for_view",
+ af.alignPanel.getViewName());
+ }
+ else
+ {
+ title = MessageManager.getString("label.sequence_feature_settings");
+ }
+ if (bounds == null)
+ {
+ if (Platform.isAMacAndNotJS())
+ {
+ Desktop.addInternalFrame(frame, title, 600, 480);
+ }
+ else
+ {
+ Desktop.addInternalFrame(frame, title, 600, 450);
+ }
+ }
+ else
+ {
+ Desktop.addInternalFrame(frame, title, false, bounds.width,
+ bounds.height);
+ frame.setBounds(bounds);
+ frame.setVisible(true);
+ }
frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
frame.addInternalFrameListener(
public void internalFrameClosed(
javax.swing.event.InternalFrameEvent evt)
{
- fr.removePropertyChangeListener(change);
- }
+ featureSettings_isClosed();
+ };
});
frame.setLayer(JLayeredPane.PALETTE_LAYER);
+ }
inConstruction = false;
}
/**
- * Constructs and shows a popup menu of possible actions on the selected row
- * and feature type
+ * Sets the state of buttons to show complement features from viewport
+ * settings
+ */
+ private void updateComplementButtons()
+ {
+ showComplement.setSelected(af.getViewport().isShowComplementFeatures());
+ showComplementOnTop
+ .setSelected(af.getViewport().isShowComplementFeaturesOnTop());
+ }
+
+ @Override
+ public AlignViewControllerGuiI getAlignframe()
+ {
+ return af;
+ }
+
+ @Override
+ public void featureSettings_isClosed()
+ {
+ fr.removePropertyChangeListener(change);
+ change = null;
+ }
+
+ /**
+ * Constructs and shows a popup menu of possible actions on the selected row and
+ * feature type
*
* @param rowSelected
* @param type
* @param typeCol
* @param pt
*/
- protected void showPopupMenu(final int rowSelected, final String type,
- final Object typeCol, final Point pt)
+ protected void showPopupMenu(final int rowSelected, final String type, final Object typeCol, final Point pt)
{
JPopupMenu men = new JPopupMenu(MessageManager
.formatMessage("label.settings_for_param", new String[]
{
if (e.getSource() == variableColourCB)
{
- men.setVisible(true); // BH 2018 for JavaScript because this is a
- // checkbox
- men.setVisible(false); // BH 2018 for JavaScript because this is a
- // checkbox
+ // BH 2018 for JavaScript because this is a checkbox
+ men.setVisible(true);
+ men.setVisible(false);
if (featureColour.isSimpleColour())
{
/*
@Override
public void actionPerformed(ActionEvent e)
{
- af.avc.sortAlignmentByFeatureScore(Arrays.asList(new String[]
- { type }));
+ sortByScore(Arrays.asList(new String[] { type }));
}
});
JMenuItem dens = new JMenuItem(
@Override
public void actionPerformed(ActionEvent e)
{
- af.avc.sortAlignmentByFeatureDensity(Arrays.asList(new String[]
- { type }));
+ sortByDensity(Arrays.asList(new String[] { type }));
}
});
men.add(dens);
{
fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
false, type);
+ fr.ap.av.sendSelection();
}
});
JMenuItem clearCols = new JMenuItem(MessageManager
{
fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
false, type);
+ fr.ap.av.sendSelection();
}
});
JMenuItem hideCols = new JMenuItem(
public void actionPerformed(ActionEvent arg0)
{
fr.ap.alignFrame.hideFeatureColumns(type, true);
+ fr.ap.av.sendSelection();
}
});
JMenuItem hideOtherCols = new JMenuItem(
public void actionPerformed(ActionEvent arg0)
{
fr.ap.alignFrame.hideFeatureColumns(type, false);
+ fr.ap.av.sendSelection();
}
});
men.add(selCols);
men.show(table, pt.x, pt.y);
}
+ /**
+ * Sort the sequences in the alignment by the number of features for the given
+ * feature types (or all features if null)
+ *
+ * @param featureTypes
+ */
+ protected void sortByDensity(List<String> featureTypes)
+ {
+ af.avc.sortAlignmentByFeatureDensity(featureTypes);
+ }
+
+ /**
+ * Sort the sequences in the alignment by average score for the given feature
+ * types (or all features if null)
+ *
+ * @param featureTypes
+ */
+ protected void sortByScore(List<String> featureTypes)
+ {
+ af.avc.sortAlignmentByFeatureScore(featureTypes);
+ }
+
+ /**
+ * Returns true if at least one feature type is visible. Else shows a warning
+ * dialog and returns false.
+ *
+ * @param title
+ * @return
+ */
+ private boolean canSortBy(String title)
+ {
+ if (fr.getDisplayedFeatureTypes().isEmpty())
+ {
+ JvOptionPane.showMessageDialog(this,
+ MessageManager.getString("label.no_features_to_sort_by"),
+ title, JvOptionPane.OK_OPTION);
+ return false;
+ }
+ return true;
+ }
+
@Override
synchronized public void discoverAllFeatureData()
{
{
fr.setGroupVisibility(check.getText(), check.isSelected());
resetTable(new String[] { grp });
- af.alignPanel.paintAlignment(true, true);
+ refreshDisplay();
}
});
groupPanel.add(check);
data[dataIndex][FILTER_COLUMN] = featureFilter == null
? new FeatureMatcherSet()
: featureFilter;
- data[dataIndex][SHOW_COLUMN] = new Boolean(
+ data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(
af.getViewport().getFeaturesDisplayed().isVisible(type));
dataIndex++;
displayableTypes.remove(type);
data[dataIndex][FILTER_COLUMN] = featureFilter == null
? new FeatureMatcherSet()
: featureFilter;
- data[dataIndex][SHOW_COLUMN] = new Boolean(true);
+ data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(true);
dataIndex++;
displayableTypes.remove(type);
}
table.repaint();
}
+ /**
+ * close ourselves but leave any existing UI handlers (e.g a CDS/Protein tabbed
+ * feature settings dialog) intact
+ */
+ public void closeOldSettings()
+ {
+ closeDialog(false);
+ }
+
+ /**
+ * close the feature settings dialog (and any containing frame)
+ */
public void close()
{
+ closeDialog(true);
+ }
+
+ private void closeDialog(boolean closeContainingFrame)
+ {
try
{
+ if (frame != null)
+ {
+ af.setFeatureSettingsGeometry(frame.getBounds());
frame.setClosed(true);
+ }
+ else
+ {
+ SplitContainerI sc = af.getSplitViewContainer();
+ sc.closeFeatureSettings(this, closeContainingFrame);
+ af.featureSettings = null;
+ }
} catch (Exception exe)
{
}
}
- /**
- * Update the priority order of features; only repaint if this changed the
- * order of visible features. Any newly discovered feature types are set to
- * visible. Returns true if repaint was requested, false if not.
- *
- * @param data
- * @return
- */
- public boolean updateFeatureRenderer(Object[][] data)
+ public void updateFeatureRenderer(Object[][] data)
{
- return updateFeatureRenderer(data, true);
+ updateFeatureRenderer(data, true);
}
/**
* Update the priority order of features; only repaint if this changed the
- * order of visible features. Returns true if repaint was requested, false if
- * not.
+ * order of visible features
*
* @param data
* @param visibleNew
- * @return
*/
- boolean updateFeatureRenderer(Object[][] data, boolean visibleNew)
+ void updateFeatureRenderer(Object[][] data, boolean visibleNew)
{
FeatureSettingsBean[] rowData = getTableAsBeans(data);
if (fr.setFeaturePriority(rowData, visibleNew))
{
- af.alignPanel.paintAlignment(true, true);
- return true;
+ refreshDisplay();
}
- return false;
}
/**
{
this.setLayout(new BorderLayout());
+ final boolean hasComplement = af.getViewport()
+ .getCodingComplement() != null;
+
JPanel settingsPane = new JPanel();
settingsPane.setLayout(new BorderLayout());
}
});
- JButton sortByScore = new JButton(
- MessageManager.getString("label.seq_sort_by_score"));
+ final String byScoreLabel = MessageManager.getString("label.seq_sort_by_score");
+ JButton sortByScore = new JButton(byScoreLabel);
sortByScore.setFont(JvSwingUtils.getLabelFont());
sortByScore.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
- af.avc.sortAlignmentByFeatureScore(null);
+ if (canSortBy(byScoreLabel))
+ {
+ sortByScore(null);
+ }
}
});
- JButton sortByDens = new JButton(
- MessageManager.getString("label.sequence_sort_by_density"));
+ final String byDensityLabel = MessageManager.getString("label.sequence_sort_by_density");
+ JButton sortByDens = new JButton(byDensityLabel);
sortByDens.setFont(JvSwingUtils.getLabelFont());
sortByDens.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
- af.avc.sortAlignmentByFeatureDensity(null);
+ if (canSortBy(byDensityLabel))
+ {
+ sortByDensity(null);
+ }
}
});
}
}
});
-
- JButton cancel = new JButton(MessageManager.getString("action.cancel"));
+ // Cancel for a SplitFrame should just revert changes to the currently displayed
+ // settings. May want to do this for either or both - so need a splitview
+ // feature settings cancel/OK.
+ JButton cancel = new JButton(MessageManager
+ .getString(hasComplement ? "action.revert" : "action.cancel"));
+ cancel.setToolTipText(MessageManager.getString(hasComplement
+ ? "action.undo_changes_to_feature_settings"
+ : "action.undo_changes_to_feature_settings_and_close_the_dialog"));
cancel.setFont(JvSwingUtils.getLabelFont());
+ // TODO: disable cancel (and apply!) until current settings are different
cancel.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
- cancel();
+ revert();
+ refreshDisplay();
+ if (!hasComplement)
+ {
+ close();
+ }
}
});
-
- JButton ok = new JButton(MessageManager.getString("action.ok"));
+ // Cancel for the whole dialog should cancel both CDS and Protein.
+ // OK for an individual feature settings just applies changes, but dialog
+ // remains open
+ JButton ok = new JButton(MessageManager
+ .getString(hasComplement ? "action.apply" : "action.ok"));
ok.setFont(JvSwingUtils.getLabelFont());
ok.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
+ if (!hasComplement)
+ {
close();
}
+ else
+ {
+ storeOriginalSettings();
+ }
+ }
});
JButton loadColours = new JButton(
if (!inConstruction)
{
fr.setTransparency((100 - transparency.getValue()) / 100f);
- af.alignPanel.paintAlignment(true, true);
+ refreshDisplay();
}
}
});
transparency.setToolTipText(
MessageManager.getString("label.transparency_tip"));
- JPanel transPanel = new JPanel(new GridLayout(1, 2));
- bigPanel.add(transPanel, BorderLayout.SOUTH);
+ boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
+ String text = MessageManager.formatMessage("label.show_linked_features",
+ nucleotide
+ ? MessageManager.getString("label.protein")
+ .toLowerCase()
+ : "CDS");
+ showComplement = new JCheckBox(text);
+ showComplement.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ af.getViewport()
+ .setShowComplementFeatures(showComplement.isSelected());
+ refreshDisplay();
+ }
+ });
+
+ showComplementOnTop = new JCheckBox(
+ MessageManager.getString("label.on_top"));
+ showComplementOnTop.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ af.getViewport().setShowComplementFeaturesOnTop(
+ showComplementOnTop.isSelected());
+ refreshDisplay();
+ }
+ });
+
+ updateComplementButtons();
+
+ JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
+ bigPanel.add(lowerPanel, BorderLayout.SOUTH);
JPanel transbuttons = new JPanel(new GridLayout(5, 1));
transbuttons.add(optimizeOrder);
transbuttons.add(sortByScore);
transbuttons.add(sortByDens);
transbuttons.add(help);
- transPanel.add(transparency);
- transPanel.add(transbuttons);
+
+ JPanel transPanelLeft = new JPanel(
+ new GridLayout(hasComplement ? 4 : 2, 1));
+ transPanelLeft.add(new JLabel(" Colour transparency" + ":"));
+ transPanelLeft.add(transparency);
+ if (hasComplement)
+ {
+ JPanel cp = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ cp.add(showComplement);
+ cp.add(showComplementOnTop);
+ transPanelLeft.add(cp);
+ }
+ lowerPanel.add(transPanelLeft);
+ lowerPanel.add(transbuttons);
JPanel buttonPanel = new JPanel();
buttonPanel.add(ok);
}
/**
- * On Cancel, restore settings as they were when the dialog was opened (or
- * possibly with any new features added while the dialog was open)
+ * Repaints alignment, structure and overview (if shown). If there is a
+ * complementary view which is showing this view's features, then also
+ * repaints that.
*/
- void cancel()
+ void refreshDisplay()
{
- fr.setTransparency(originalTransparency);
- fr.setFeatureFilters(originalFilters);
- boolean repainted = updateFeatureRenderer(originalData);
-
- /*
- * ensure alignment (and Overview if visible) are redrawn
- */
- if (!repainted)
+ af.alignPanel.paintAlignment(true, true);
+ AlignViewportI complement = af.getViewport().getCodingComplement();
+ if (complement != null && complement.isShowComplementFeatures())
{
- af.alignPanel.paintAlignment(true, true);
+ AlignFrame af2 = Desktop.getAlignFrameFor(complement);
+ af2.alignPanel.paintAlignment(true, true);
}
-
- close();
}
/**
}
/**
- * Answers the class of the object in column c of the first row of the table
+ * Answers the class of column c of the table
*/
@Override
public Class<?> getColumnClass(int c)
{
- Object v = getValueAt(0, c);
- return v == null ? null : v.getClass();
+ switch (c)
+ {
+ case TYPE_COLUMN:
+ return String.class;
+ case COLOUR_COLUMN:
+ return FeatureColour.class;
+ case FILTER_COLUMN:
+ return FeatureMatcherSet.class;
+ default:
+ return Boolean.class;
+ }
}
@Override
return button;
}
}
+
+ public boolean isOpen()
+ {
+ if (af.getSplitViewContainer() != null)
+ {
+ return af.getSplitViewContainer().isFeatureSettingsOpen();
+ }
+ return frame != null && !frame.isClosed();
+ }
+
+ @Override
+ public void revert()
+ {
+ fr.setTransparency(originalTransparency);
+ fr.setFeatureFilters(originalFilters);
+ updateFeatureRenderer(originalData);
+ af.getViewport().setViewStyle(originalViewStyle);
+ updateTransparencySliderFromFR();
+ updateComplementButtons();
+ refreshDisplay();
+ }
}
class FeatureIcon implements Icon
import jalview.datamodel.AlignmentAnnotation;
import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceGroup;
import jalview.datamodel.SequenceI;
import jalview.gui.SeqPanel.MousePos;
}
Sequence sq = (Sequence) av.getAlignment().getSequenceAt(pos.seqIndex);
-
- /*
- * build a new links menu based on the current links
- * and any non-positional features
- */
- List<SequenceFeature> features = null;
if (sq != null)
{
- List<String> nlinks = Preferences.sequenceUrlLinks.getLinksForMenu();
- features = sq.getFeatures().getNonPositionalFeatures();
- for (SequenceFeature sf : features)
- {
- if (sf.links != null)
- {
- nlinks.addAll(sf.links);
+ PopupMenu pop = new PopupMenu(alignPanel, sq,
+ Preferences.getGroupURLLinks());
+ pop.show(this, e.getX(), e.getY());
}
}
- }
-
- PopupMenu pop = new PopupMenu(alignPanel, sq, features,
- Preferences.getGroupURLLinks() // empty list; not implemented
- );
- pop.show(this, e.getX(), e.getY());
- }
/**
* On right mouse click on a Consensus annotation label, shows a limited popup
import jalview.analysis.AlignmentAnnotationUtils;
import jalview.analysis.AlignmentUtils;
import jalview.analysis.Conservation;
+import jalview.api.AlignViewportI;
import jalview.bin.Cache;
import jalview.commands.ChangeCaseCommand;
import jalview.commands.EditCommand;
import jalview.commands.EditCommand.Action;
import jalview.datamodel.AlignmentAnnotation;
import jalview.datamodel.AlignmentI;
-import jalview.datamodel.Annotation;
import jalview.datamodel.DBRefEntry;
import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.MappedFeatures;
import jalview.datamodel.PDBEntry;
import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceGroup;
import jalview.schemes.ColourSchemes;
import jalview.schemes.PIDColourScheme;
import jalview.schemes.ResidueColourScheme;
+import jalview.util.Comparison;
import jalview.util.GroupUrlLink;
import jalview.util.GroupUrlLink.UrlStringTooLongException;
import jalview.util.MessageManager;
import jalview.util.Platform;
import jalview.util.StringUtils;
import jalview.util.UrlLink;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel;
import java.awt.BorderLayout;
import java.awt.Color;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Vector;
*/
public class PopupMenu extends JPopupMenu implements ColourChangeListener
{
+ /*
+ * maximum length of feature description to include in popup menu item text
+ */
+ private static final int FEATURE_DESC_MAX = 40;
+
+ /*
+ * true for ID Panel menu, false for alignment panel menu
+ */
+ private final boolean forIdPanel;
+
+ private final AlignmentPanel ap;
+
+ /*
+ * the sequence under the cursor when clicked
+ * (additional sequences may be selected)
+ */
+ private final SequenceI sequence;
+
JMenu groupMenu = new JMenu();
JMenuItem groupName = new JMenuItem();
protected JMenuItem modifyConservation = new JMenuItem();
- AlignmentPanel ap;
-
JMenu sequenceMenu = new JMenu();
- JMenuItem sequenceName = new JMenuItem();
-
- JMenuItem sequenceDetails = new JMenuItem();
-
- JMenuItem sequenceSelDetails = new JMenuItem();
-
JMenuItem makeReferenceSeq = new JMenuItem();
- JMenuItem chooseAnnotations = new JMenuItem();
-
- SequenceI sequence;
-
JMenuItem createGroupMenuItem = new JMenuItem();
JMenuItem unGroupMenuItem = new JMenuItem();
- JMenuItem outline = new JMenuItem();
-
JMenu colourMenu = new JMenu();
JCheckBoxMenuItem showBoxes = new JCheckBoxMenuItem();
JMenu editMenu = new JMenu();
- JMenuItem cut = new JMenuItem();
-
- JMenuItem copy = new JMenuItem();
-
JMenuItem upperCase = new JMenuItem();
JMenuItem lowerCase = new JMenuItem();
JMenuItem toggle = new JMenuItem();
- JMenu pdbMenu = new JMenu();
-
JMenu outputMenu = new JMenu();
JMenu seqShowAnnotationsMenu = new JMenu();
JMenuItem groupAddReferenceAnnotations = new JMenuItem(
MessageManager.getString("label.add_reference_annotations"));
- JMenuItem sequenceFeature = new JMenuItem();
-
JMenuItem textColour = new JMenuItem();
- JMenu jMenu1 = new JMenu();
+ JMenu editGroupMenu = new JMenu();
- JMenuItem pdbStructureDialog = new JMenuItem();
+ JMenuItem chooseStructure = new JMenuItem();
JMenu rnaStructureMenu = new JMenu();
- JMenuItem editSequence = new JMenuItem();
-
- JMenu groupLinksMenu;
-
- JMenuItem hideInsertions = new JMenuItem();
-
/**
* Constructs a menu with sub-menu items for any hyperlinks for the sequence
* and/or features provided. Hyperlinks may include a lookup by sequence id,
* @param features
* @return
*/
- static JMenu buildLinkMenu(final SequenceI seq,
+ protected static JMenu buildLinkMenu(final SequenceI seq,
List<SequenceFeature> features)
{
JMenu linkMenu = new JMenu(MessageManager.getString("action.link"));
}
/**
- * For the popup menu on the idPanel.
- *
- * Add a late bound groupURL item to the given linkMenu
+ * add a late bound groupURL item to the given linkMenu
*
* @param linkMenu
* @param label
{
try
{
- // Object[] { int[] { number of matches seqs },
- // boolean[] { which matched },
- // StringBuffer[] { segment generated from inputs },
- // String[] { url }
- // }
-
- // TODO bug: urlstub is { int[], boolean[] } but constructFrom
- // requires something else.
-
showLink(urlgenerator.constructFrom(urlstub));
} catch (UrlStringTooLongException e2)
{
}
/**
- * Creates a new PopupMenu object.
+ * Constructor for a PopupMenu for a click in the alignment panel (on a residue)
*
* @param ap
+ * the panel in which the mouse is clicked
* @param seq
- * @param features
- * non-positional features (for seq not null), or positional features
- * at residue (for seq equal to null)
+ * the sequence under the mouse
+ * @throws NullPointerException
+ * if seq is null
*/
- public PopupMenu(final AlignmentPanel ap, SequenceI seq,
- List<SequenceFeature> features)
+ public PopupMenu(final AlignmentPanel ap, SequenceI seq, int column)
{
- this(ap, seq, features, null);
+ this(false, ap, seq, column, null);
}
/**
- * Constructor
+ * Constructor for a PopupMenu for a click in the sequence id panel
*
* @param alignPanel
+ * the panel in which the mouse is clicked
* @param seq
- * the sequence under the cursor if in the Id panel, null if in the
- * sequence panel
- * @param features
- * non-positional features if in the Id panel, features at the
- * clicked residue if in the sequence panel
+ * the sequence under the mouse click
* @param groupLinks
- * not implemented -- empty list
+ * templates for sequence external links
+ * @throws NullPointerException
+ * if seq is null
*/
public PopupMenu(final AlignmentPanel alignPanel, final SequenceI seq,
- List<SequenceFeature> features, List<String> groupLinks)
+ List<String> groupLinks)
{
- // /////////////////////////////////////////////////////////
- // If this is activated from the sequence panel, the user may want to
- // edit or annotate a particular residue. Therefore display the residue menu
- //
- // If from the IDPanel, we must display the sequence menu
- // ////////////////////////////////////////////////////////
+ this(true, alignPanel, seq, -1, groupLinks);
+ }
+
+ /**
+ * Private constructor that constructs a popup menu for either sequence ID
+ * Panel, or alignment context
+ *
+ * @param fromIdPanel
+ * @param alignPanel
+ * @param seq
+ * @param column
+ * aligned column position (0...)
+ * @param groupLinks
+ */
+ private PopupMenu(boolean fromIdPanel,
+ final AlignmentPanel alignPanel,
+ final SequenceI seq, final int column, List<String> groupLinks)
+ {
+ Objects.requireNonNull(seq);
+ this.forIdPanel = fromIdPanel;
this.ap = alignPanel;
sequence = seq;
* 'reference annotations' that may be added to the alignment. First for the
* currently selected sequence (if there is one):
*/
- final List<SequenceI> selectedSequence = (seq == null
- ? Collections.<SequenceI> emptyList()
- : Arrays.asList(seq));
+ final List<SequenceI> selectedSequence = (forIdPanel && seq != null
+ ? Arrays.asList(seq)
+ : Collections.<SequenceI> emptyList());
buildAnnotationTypesMenus(seqShowAnnotationsMenu,
seqHideAnnotationsMenu, selectedSequence);
configureReferenceAnnotationsMenu(seqAddReferenceAnnotations,
e.printStackTrace();
}
- JMenuItem menuItem;
- if (seq != null)
+ if (forIdPanel)
{
+ JMenuItem menuItem;
sequenceMenu.setText(sequence.getName());
if (seq == alignPanel.av.getAlignment().getSeqrep())
{
}
if (addOption)
{
- menuItem = new JMenuItem(
+ JMenuItem menuItem = new JMenuItem(
MessageManager.getString("action.reveal_all"));
menuItem.addActionListener(new ActionListener()
{
// add any groupURLs to the groupURL submenu and make it visible
if (groupLinks != null && groupLinks.size() > 0)
{
- // not implemented -- empty list
buildGroupURLMenu(sg, groupLinks);
}
// Add a 'show all structures' for the current selection
{
createGroupMenuItem.setVisible(true);
unGroupMenuItem.setVisible(false);
- jMenu1.setText(MessageManager.getString("action.edit_new_group"));
+ editGroupMenu.setText(MessageManager.getString("action.edit_new_group"));
}
else
{
createGroupMenuItem.setVisible(false);
unGroupMenuItem.setVisible(true);
- jMenu1.setText(MessageManager.getString("action.edit_group"));
+ editGroupMenu.setText(MessageManager.getString("action.edit_group"));
}
- if (seq == null)
+ if (!forIdPanel)
{
sequenceMenu.setVisible(false);
- pdbStructureDialog.setVisible(false);
+ chooseStructure.setVisible(false);
rnaStructureMenu.setVisible(false);
}
+ addLinksAndFeatures(seq, column);
+ }
+
+ /**
+ * Adds
+ * <ul>
+ * <li>configured sequence database links (ID panel popup menu)</li>
+ * <li>non-positional feature links (ID panel popup menu)</li>
+ * <li>positional feature links (alignment panel popup menu)</li>
+ * <li>feature details links (alignment panel popup menu)</li>
+ * </ul>
+ * If this panel is also showed complementary (CDS/protein) features, then links
+ * to their feature details are also added.
+ *
+ * @param seq
+ * @param column
+ */
+ void addLinksAndFeatures(final SequenceI seq, final int column)
+ {
+ List<SequenceFeature> features = null;
+ if (forIdPanel)
+ {
+ features = sequence.getFeatures().getNonPositionalFeatures();
+ }
+ else
+ {
+ features = ap.getFeatureRenderer().findFeaturesAtColumn(sequence,
+ column + 1);
+ }
+
addLinks(seq, features);
- if (seq == null)
+ if (!forIdPanel)
{
- addFeatureDetails(features);
+ addFeatureDetails(features, seq, column);
}
}
* Add a link to show feature details for each sequence feature
*
* @param features
+ * @param column
+ * @param seq
*/
- protected void addFeatureDetails(List<SequenceFeature> features)
+ protected void addFeatureDetails(List<SequenceFeature> features,
+ SequenceI seq, int column)
{
- if (features == null || features.isEmpty())
+ /*
+ * add features in CDS/protein complement at the corresponding
+ * position if configured to do so
+ */
+ MappedFeatures mf = null;
+ if (ap.av.isShowComplementFeatures())
{
+ if (!Comparison.isGap(sequence.getCharAt(column)))
+ {
+ AlignViewportI complement = ap.getAlignViewport()
+ .getCodingComplement();
+ AlignFrame af = Desktop.getAlignFrameFor(complement);
+ FeatureRendererModel fr2 = af.getFeatureRenderer();
+ int seqPos = sequence.findPosition(column);
+ mf = fr2.findComplementFeaturesAtResidue(sequence, seqPos);
+ }
+ }
+
+ if (features.isEmpty() && mf == null)
+ {
+ /*
+ * no features to show at this position
+ */
return;
}
JMenu details = new JMenu(
MessageManager.getString("label.feature_details"));
add(details);
+ String name = seq.getName();
for (final SequenceFeature sf : features)
{
- int start = sf.getBegin();
- int end = sf.getEnd();
- String desc = null;
- if (start == end)
+ addFeatureDetailsMenuItem(details, name, sf);
+ }
+
+ if (mf != null)
+ {
+ name = mf.fromSeq == seq ? mf.mapping.getTo().getName()
+ : mf.fromSeq.getName();
+ for (final SequenceFeature sf : mf.features)
{
- desc = String.format("%s %d", sf.getType(), start);
+ addFeatureDetailsMenuItem(details, name, sf);
}
- else
+ }
+ }
+
+ /**
+ * A helper method to add one menu item whose action is to show details for one
+ * feature. The menu text includes feature description, but this may be
+ * truncated.
+ *
+ * @param details
+ * @param seqName
+ * @param sf
+ */
+ void addFeatureDetailsMenuItem(JMenu details, final String seqName,
+ final SequenceFeature sf)
+ {
+ int start = sf.getBegin();
+ int end = sf.getEnd();
+ StringBuilder desc = new StringBuilder();
+ desc.append(sf.getType()).append(" ").append(String.valueOf(start));
+ if (start != end)
{
- desc = String.format("%s %d-%d", sf.getType(), start, end);
+ desc.append("-").append(String.valueOf(end));
}
- String tooltip = desc;
String description = sf.getDescription();
if (description != null)
{
+ desc.append(" ");
description = StringUtils.stripHtmlTags(description);
- if (description.length() > 12)
+
+ /*
+ * truncate overlong descriptions unless they contain an href
+ * (as truncation could leave corrupted html)
+ */
+ boolean hasLink = description.indexOf("a href") > -1;
+ if (description.length() > FEATURE_DESC_MAX && !hasLink)
{
- desc = desc + " " + description.substring(0, 12) + "..";
+ description = description.substring(0, FEATURE_DESC_MAX) + "...";
}
- else
- {
- desc = desc + " " + description;
+ desc.append(description);
}
- tooltip = tooltip + " " + description;
- }
- if (sf.getFeatureGroup() != null)
+ String featureGroup = sf.getFeatureGroup();
+ if (featureGroup != null)
{
- tooltip = tooltip + (" (" + sf.getFeatureGroup() + ")");
+ desc.append(" (").append(featureGroup).append(")");
}
- JMenuItem item = new JMenuItem(desc);
- item.setToolTipText(tooltip);
+ String htmlText = JvSwingUtils.wrapTooltip(true, desc.toString());
+ JMenuItem item = new JMenuItem(htmlText);
item.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
- showFeatureDetails(sf);
+ showFeatureDetails(seqName, sf);
}
});
details.add(item);
}
- }
/**
* Opens a panel showing a text report of feature dteails
*
+ * @param seqName
+ *
* @param sf
*/
- protected void showFeatureDetails(SequenceFeature sf)
+ protected void showFeatureDetails(String seqName, SequenceFeature sf)
{
JInternalFrame details;
if (Platform.isJS())
// TODO JAL-3026 set style of table correctly for feature details
JLabel reprt = new JLabel(MessageManager
.formatMessage("label.html_content", new Object[]
- { sf.getDetailsReport() }));
+ { sf.getDetailsReport(seqName) }));
reprt.setBackground(Color.WHITE);
reprt.setOpaque(true);
panel.add(reprt, BorderLayout.CENTER);
// it appears Java's CSS does not support border-collaps :-(
cap.addStylesheetRule("table { border-collapse: collapse;}");
cap.addStylesheetRule("table, td, th {border: 1px solid black;}");
- cap.setText(sf.getDetailsReport());
+ cap.setText(sf.getDetailsReport(seqName));
details = cap;
}
Desktop.addInternalFrame(details,
*/
void addLinks(final SequenceI seq, List<SequenceFeature> features)
{
- JMenu linkMenu = buildLinkMenu(seq, features);
+ JMenu linkMenu = buildLinkMenu(forIdPanel ? seq : null, features);
// only add link menu if it has entries
if (linkMenu.getItemCount() > 0)
{
- if (sequence != null)
+ if (forIdPanel)
{
sequenceMenu.add(linkMenu);
}
showOrHideMenu.add(item);
}
- /**
- *
- * @param sg
- * @param groupLinks
- * not implemented -- empty list
- */
private void buildGroupURLMenu(SequenceGroup sg, List<String> groupLinks)
{
// menu appears asap
// sequence only URLs
// ID/regex match URLs
- groupLinksMenu = new JMenu(
+ JMenu groupLinksMenu = new JMenu(
MessageManager.getString("action.group_link"));
// three types of url that might be created.
JMenu[] linkMenus = new JMenu[] { null,
}
}
}
- if (groupLinks.size() == 0)
- {
- return;
- }
// now create group links for all distinct ID/sequence sets.
boolean addMenu = false; // indicates if there are any group links to give
// to user
-
- // not implmeented -- empty list
for (String link : groupLinks)
{
GroupUrlLink urlLink = null;
Cache.log.error("Exception for GroupURLLink '" + link + "'", foo);
continue;
}
- ;
if (!urlLink.isValid())
{
Cache.log.error(urlLink.getInvalidMessage());
{
urlset = urlLink.makeUrlStubs(ids, seqstr,
"FromJalview" + System.currentTimeMillis(), false);
- // { int[], boolean[] } only here
} catch (UrlStringTooLongException e)
{
}
}
});
sequenceMenu.setText(MessageManager.getString("label.sequence"));
- sequenceName.setText(
+
+ JMenuItem sequenceName = new JMenuItem(
MessageManager.getString("label.edit_name_description"));
sequenceName.addActionListener(new ActionListener()
{
sequenceName_actionPerformed();
}
});
- chooseAnnotations
- .setText(MessageManager.getString("action.choose_annotations"));
+ JMenuItem chooseAnnotations = new JMenuItem(
+ MessageManager.getString("action.choose_annotations"));
chooseAnnotations.addActionListener(new ActionListener()
{
@Override
chooseAnnotations_actionPerformed(e);
}
});
- sequenceDetails
- .setText(MessageManager.getString("label.sequence_details"));
+ JMenuItem sequenceDetails = new JMenuItem(
+ MessageManager.getString("label.sequence_details"));
sequenceDetails.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
- sequenceDetails_actionPerformed();
+ createSequenceDetailsReport(new SequenceI[] { sequence });
}
});
- sequenceSelDetails
- .setText(MessageManager.getString("label.sequence_details"));
+ JMenuItem sequenceSelDetails = new JMenuItem(
+ MessageManager.getString("label.sequence_details"));
sequenceSelDetails.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
- sequenceSelectionDetails_actionPerformed();
+ createSequenceDetailsReport(ap.av.getSequenceSelection());
}
});
}
});
- outline.setText(MessageManager.getString("action.border_colour"));
+ JMenuItem outline = new JMenuItem(
+ MessageManager.getString("action.border_colour"));
outline.addActionListener(new ActionListener()
{
@Override
}
});
editMenu.setText(MessageManager.getString("action.edit"));
- cut.setText(MessageManager.getString("action.cut"));
+ JMenuItem cut = new JMenuItem(MessageManager.getString("action.cut"));
cut.addActionListener(new ActionListener()
{
@Override
changeCase(e);
}
});
- copy.setText(MessageManager.getString("action.copy"));
+ JMenuItem copy = new JMenuItem(MessageManager.getString("action.copy"));
copy.addActionListener(new ActionListener()
{
@Override
.setText(MessageManager.getString("label.show_annotations"));
groupHideAnnotationsMenu
.setText(MessageManager.getString("label.hide_annotations"));
- sequenceFeature.setText(
+ JMenuItem sequenceFeature = new JMenuItem(
MessageManager.getString("label.create_sequence_feature"));
sequenceFeature.addActionListener(new ActionListener()
{
sequenceFeature_actionPerformed();
}
});
- jMenu1.setText(MessageManager.getString("label.group"));
- pdbStructureDialog.setText(
+ editGroupMenu.setText(MessageManager.getString("label.group"));
+ chooseStructure.setText(
MessageManager.getString("label.show_pdbstruct_dialog"));
- pdbStructureDialog.addActionListener(new ActionListener()
+ chooseStructure.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent actionEvent)
.setText(MessageManager.getString("label.view_rna_structure"));
// colStructureMenu.setText("Colour By Structure");
- editSequence.setText(
+ JMenuItem editSequence = new JMenuItem(
MessageManager.getString("label.edit_sequence") + "...");
editSequence.addActionListener(new ActionListener()
{
}
});
- hideInsertions
- .setText(MessageManager.getString("label.hide_insertions"));
+
+ groupMenu.add(sequenceSelDetails);
+ add(groupMenu);
+ add(sequenceMenu);
+ add(rnaStructureMenu);
+ add(chooseStructure);
+ if (forIdPanel)
+ {
+ JMenuItem hideInsertions = new JMenuItem(
+ MessageManager.getString("label.hide_insertions"));
hideInsertions.addActionListener(new ActionListener()
{
hideInsertions_actionPerformed(e);
}
});
-
- groupMenu.add(sequenceSelDetails);
- add(groupMenu);
- add(sequenceMenu);
- add(rnaStructureMenu);
- add(pdbStructureDialog);
- if (sequence != null)
- {
add(hideInsertions);
}
// annotations configuration panel suppressed for now
groupMenu.add(sequenceFeature);
groupMenu.add(createGroupMenuItem);
groupMenu.add(unGroupMenuItem);
- groupMenu.add(jMenu1);
+ groupMenu.add(editGroupMenu);
sequenceMenu.add(sequenceName);
sequenceMenu.add(sequenceDetails);
sequenceMenu.add(makeReferenceSeq);
editMenu.add(upperCase);
editMenu.add(lowerCase);
editMenu.add(toggle);
- // JBPNote: These shouldn't be added here - should appear in a generic
- // 'apply web service to this sequence menu'
- // pdbMenu.add(RNAFold);
- // pdbMenu.add(ContraFold);
- jMenu1.add(groupName);
- jMenu1.add(colourMenu);
- jMenu1.add(showBoxes);
- jMenu1.add(showText);
- jMenu1.add(showColourText);
- jMenu1.add(outline);
- jMenu1.add(displayNonconserved);
+ editGroupMenu.add(groupName);
+ editGroupMenu.add(colourMenu);
+ editGroupMenu.add(showBoxes);
+ editGroupMenu.add(showText);
+ editGroupMenu.add(showColourText);
+ editGroupMenu.add(outline);
+ editGroupMenu.add(displayNonconserved);
}
/**
createSequenceDetailsReport(ap.av.getSequenceSelection());
}
- protected void sequenceDetails_actionPerformed()
- {
- createSequenceDetailsReport(new SequenceI[] { sequence });
- }
-
public void createSequenceDetailsReport(SequenceI[] sequences)
{
StringBuilder contents = new StringBuilder(128);
}
/**
- * Shows a dialog where sequence name and description may be edited
+ * Shows a dialog where the sequence name and description may be edited. If a
+ * name containing spaces is entered, these are converted to underscores, with a
+ * warning message.
*/
void sequenceName_actionPerformed()
{
{
getGroup().setOutlineColour(c);
refresh();
+ }
};
- };
JalviewColourChooser.showColourChooser(Desktop.getDesktopPane(),
title, Color.BLUE, listener);
}
}
}
- public void colourByStructure(String pdbid)
- {
- Annotation[] anots = ap.av.getStructureSelectionManager()
- .colourSequenceFromStructure(sequence, pdbid);
-
- AlignmentAnnotation an = new AlignmentAnnotation("Structure",
- "Coloured by " + pdbid, anots);
-
- ap.av.getAlignment().addAnnotation(an);
- an.createSequenceMapping(sequence, 0, true);
- // an.adjustForAlignment();
- ap.av.getAlignment().setAnnotationIndex(an, 0);
-
- ap.adjustAnnotationHeight();
-
- sequence.addAlignmentAnnotation(an);
-
- }
-
/**
* Shows a dialog where sequence characters may be edited. Any changes are
* applied, and added as an available 'Undo' item in the edit commands
{
SequenceGroup sg = ap.av.getSelectionGroup();
+ SequenceI seq = sequence;
if (sg != null)
{
- if (sequence == null)
+ if (seq == null)
{
- sequence = sg.getSequenceAt(0);
+ seq = sg.getSequenceAt(0);
}
EditNameDialog dialog = new EditNameDialog(
- sequence.getSequenceAsString(sg.getStartRes(),
- sg.getEndRes() + 1),
+ seq.getSequenceAsString(sg.getStartRes(), sg.getEndRes() + 1),
null, MessageManager.getString("label.edit_sequence"), null);
dialog.showDialog(ap.alignFrame,
MessageManager.getString("label.edit_sequence"),
import jalview.datamodel.AlignmentI;
import jalview.datamodel.ColumnSelection;
import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.MappedFeatures;
import jalview.datamodel.SearchResultMatchI;
import jalview.datamodel.SearchResults;
import jalview.datamodel.SearchResultsI;
import jalview.util.Platform;
import jalview.viewmodel.AlignmentViewport;
import jalview.viewmodel.ViewportRanges;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
SearchResultsI lastSearchResults;
/**
- * Constructor
+ * Creates a new SeqPanel object
*
* @param viewport
* @param alignPanel
* the start of the highlighted region.
*/
@Override
- public void highlightSequence(SearchResultsI results)
+ public String highlightSequence(SearchResultsI results)
{
if (results == null || results.equals(lastSearchResults))
{
- return;
+ return null;
}
lastSearchResults = results;
{
setStatusMessage(results);
}
+ return results.isEmpty() ? null : getHighlightInfo(results);
+ }
+
+ /**
+ * temporary hack: answers a message suitable to show on structure hover
+ * label. This is normally null. It is a peptide variation description if
+ * <ul>
+ * <li>results are a single residue in a protein alignment</li>
+ * <li>there is a mapping to a coding sequence (codon)</li>
+ * <li>there are one or more SNP variant features on the codon</li>
+ * </ul>
+ * in which case the answer is of the format (e.g.) "p.Glu388Asp"
+ *
+ * @param results
+ * @return
+ */
+ private String getHighlightInfo(SearchResultsI results)
+ {
+ /*
+ * ideally, just find mapped CDS (as we don't care about render style here);
+ * for now, go via split frame complement's FeatureRenderer
+ */
+ AlignViewportI complement = ap.getAlignViewport().getCodingComplement();
+ if (complement == null)
+ {
+ return null;
+ }
+ AlignFrame af = Desktop.getAlignFrameFor(complement);
+ FeatureRendererModel fr2 = af.getFeatureRenderer();
+
+ int j = results.getSize();
+ List<String> infos = new ArrayList<>();
+ for (int i = 0; i < j; i++)
+ {
+ SearchResultMatchI match = results.getResults().get(i);
+ int pos = match.getStart();
+ if (pos == match.getEnd())
+ {
+ SequenceI seq = match.getSequence();
+ SequenceI ds = seq.getDatasetSequence() == null ? seq
+ : seq.getDatasetSequence();
+ MappedFeatures mf = fr2
+ .findComplementFeaturesAtResidue(ds, pos);
+ if (mf != null)
+ {
+ for (SequenceFeature sf : mf.features)
+ {
+ String pv = mf.findProteinVariants(sf);
+ if (pv.length() > 0 && !infos.contains(pv))
+ {
+ infos.add(pv);
+ }
+ }
+ }
+ }
+ }
+
+ if (infos.isEmpty())
+ {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (String info : infos)
+ {
+ if (sb.length() > 0)
+ {
+ sb.append("|");
+ }
+ sb.append(info);
+ }
+ return sb.toString();
}
@Override
* add features that straddle the gap (pos may be the residue before or
* after the gap)
*/
+ int unshownFeatures = 0;
if (av.isShowSequenceFeatures())
{
List<SequenceFeature> features = ap.getFeatureRenderer()
.findFeaturesAtColumn(sequence, column + 1);
- seqARep.appendFeatures(tooltipText, pos, features,
- this.ap.getSeqPanel().seqCanvas.fr);
+ unshownFeatures = seqARep.appendFeaturesLengthLimit(tooltipText, pos,
+ features,
+ this.ap.getSeqPanel().seqCanvas.fr, MAX_TOOLTIP_LENGTH);
+
+ /*
+ * add features in CDS/protein complement at the corresponding
+ * position if configured to do so
+ */
+ if (av.isShowComplementFeatures())
+ {
+ if (!Comparison.isGap(sequence.getCharAt(column)))
+ {
+ AlignViewportI complement = ap.getAlignViewport()
+ .getCodingComplement();
+ AlignFrame af = Desktop.getAlignFrameFor(complement);
+ FeatureRendererModel fr2 = af.getFeatureRenderer();
+ MappedFeatures mf = fr2.findComplementFeaturesAtResidue(sequence,
+ pos);
+ if (mf != null)
+ {
+ unshownFeatures = seqARep.appendFeaturesLengthLimit(
+ tooltipText, pos, mf, fr2,
+ MAX_TOOLTIP_LENGTH);
+ }
+ }
+ }
}
- if (tooltipText.length() == 6) // <html>
+ if (tooltipText.length() == 6) // "<html>"
{
setToolTipText(null);
lastTooltip = null;
}
else
{
- if (tooltipText.length() > MAX_TOOLTIP_LENGTH) // constant
+ if (tooltipText.length() > MAX_TOOLTIP_LENGTH)
{
tooltipText.setLength(MAX_TOOLTIP_LENGTH);
tooltipText.append("...");
}
+ if (unshownFeatures > 0)
+ {
+ tooltipText.append("<br/>").append("... ").append("<i>")
+ .append(MessageManager.formatMessage(
+ "label.features_not_shown", unshownFeatures))
+ .append("</i>");
+ }
String textString = tooltipText.toString();
if (lastTooltip == null || !lastTooltip.equals(textString))
{
return;
}
- /*
- * start scrolling if mouse dragging, whether the drag started
- * in the scale panel or this panel
- */
- if (mouseDragging || ap.getScalePanel().isMouseDragging())
+ // BH check was:
+// /*
+// * start scrolling if mouse dragging, whether the drag started
+// * in the scale panel or this panel
+// */
+// if (mouseDragging || ap.getScalePanel().isMouseDragging())
+// {
+// startScrolling(new Point(e.getX(), 0));
+// }
+
+ if (mouseDragging && scrollThread == null)
{
- startScrolling(new Point(e.getX(), 0));
+ startScrolling(e.getPoint());
}
}
return;
}
- if (evt.getClickCount() > 1)
+ if (evt.getClickCount() > 1 && av.isShowSequenceFeatures())
{
sg = av.getSelectionGroup();
if (sg != null && sg.getSize() == 1
final int column = pos.column;
final int seq = pos.seqIndex;
SequenceI sequence = av.getAlignment().getSequenceAt(seq);
- List<SequenceFeature> features = ap.getFeatureRenderer()
- .findFeaturesAtColumn(sequence, column + 1);
-
- PopupMenu pop = new PopupMenu(ap, null, features);
+ if (sequence != null)
+ {
+ PopupMenu pop = new PopupMenu(ap, sequence, column);
pop.show(this, evt.getX(), evt.getY());
}
+ }
/**
* Update the display after mouse up on a selection or group
// if (!scrollOnce() {t.stop();}) gives compiler error :-(
scrollThread.scrollOnce();
}
+ }
+ });
+ t.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
if (scrollThread == null)
{
// SeqPanel.stopScrolling called
- ((Timer) e.getSource()).stop();
+ t.stop();
}
}
});
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
-import java.awt.Insets;
import java.awt.MediaTracker;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
+import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
-import java.beans.PropertyVetoException;
+import java.net.URL;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JTextPane;
-import javax.swing.Timer;
-import javax.swing.border.EmptyBorder;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
/**
- * A class that serves both as an initial 5-second splash screen (interactive
- * false) as well as for the Help menu item action (interactive true).
- *
- * As a splash screen, the frame closes if clicked by the user.
- *
- * Closure loop converted from a while/sleep loop to a JavaScript-compatible
- * state machine by Bob Hanson 2019.11.26.
- *
- * TODO: get JTextPane working for read-only HTML.
+ * DOCUMENT ME!
*
+ * @author $author$
+ * @version $Revision$
*/
-@SuppressWarnings("serial")
public class SplashScreen extends JPanel
implements Runnable, HyperlinkListener
{
- private static final int STATE_INIT = 0;
-
- private static final int STATE_LOOP = 1;
+ private static final int SHOW_FOR_SECS = 5;
- private static final int STATE_DONE = 2;
+ private static final int FONT_SIZE = 11;
- // boolean visible = true;
+ private boolean visible = true;
private JPanel iconimg = new JPanel(new BorderLayout());
- /**
- * Temporary SwingJS Hack: Either a JLabel in JavaScript or a JTextPane in
- * Java
+ /*
+ * as JTextPane in Java, JLabel in javascript
*/
- protected Component htmlPane;
+ private Component splashText;
private JInternalFrame iframe;
private Image image;
- private final static int fontSize = 11;
-
- protected final static Font largeFont = new Font("Verdana", Font.BOLD,
- fontSize + 6);
+ private boolean transientDialog = false;
- int yoffset = 30;
+ private long oldTextLength = -1;
- /**
- * Creates a new SplashScreen object.
- */
- public SplashScreen()
- {
- this(false);
- }
-
- protected boolean isInteractive = false;
-
- /**
- *
- * @param interactive
- * if true - an internal dialog is opened rather than a free-floating
- * splash screen
+ /*
+ * allow click in the initial splash screen to dismiss it
+ * immediately (not if opened from About menu)
*/
- public SplashScreen(boolean isInteractive)
- {
- this.isInteractive = isInteractive;
- Thread t = new Thread(this, "SplashScreen");
- t.start();
- }
-
- MouseAdapter closer = new MouseAdapter()
+ private MouseAdapter closer = new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent evt)
{
- try
+ if (transientDialog)
{
- if (!isInteractive)
+ try
{
- setVisible(false);
+ visible = false;
closeSplash();
+ } catch (Exception ex)
+ {
}
- } catch (Exception ex)
- {
}
}
};
/**
+ * Constructor that displays the splash screen
+ *
+ * @param isTransient
+ * if true the panel removes itself on click or after a few seconds;
+ * if false it stays up until closed by the user
+ */
+ public SplashScreen(boolean isTransient)
+ {
+ this.transientDialog = isTransient;
+
+ if (Platform.isJS()) // BH 2019
+ {
+ splashText = new JLabel("");
+ run();
+ }
+ else
+ {
+ /**
+ * Java only
+ *
+ * @j2sIgnore
+ */
+ {
+ splashText = new JTextPane();
+ Thread t = new Thread(this);
+ t.start();
+ }
+ }
+ }
+
+ /**
* ping the jalview version page then create and display the jalview
* splashscreen window.
*/
- protected void initSplashScreenWindow()
+ void initSplashScreenWindow()
{
addMouseListener(closer);
+
try
{
- java.net.URL url = getClass().getResource("/images/Jalview_Logo.png");
- image = java.awt.Toolkit.getDefaultToolkit().createImage(url);
- MediaTracker mt = new MediaTracker(this);
- mt.addImage(image, 0);
- Image logo = (Platform.isJS() ? null
- : java.awt.Toolkit.getDefaultToolkit().createImage(getClass()
- .getResource("/images/Jalview_Logo_small.png")));
- if (logo != null)
+ URL url = getClass().getResource("/images/Jalview_Logo.png");
+ URL urllogo = getClass()
+ .getResource("/images/Jalview_Logo_small.png");
+
+ if (!Platform.isJS() && url != null)
{
+ image = Toolkit.getDefaultToolkit().createImage(url);
+ Image logo = Toolkit.getDefaultToolkit().createImage(urllogo);
+ MediaTracker mt = new MediaTracker(this);
+ mt.addImage(image, 0);
mt.addImage(logo, 1);
- }
- do
- {
- try
- {
- mt.waitForAll();
- } catch (InterruptedException x)
+ do
{
- }
- if (mt.isErrorAny())
- {
- System.err.println("Error when loading images!");
- }
- } while (!mt.checkAll());
- if (url != null)
- {
+ try
+ {
+ mt.waitForAll();
+ } catch (InterruptedException x)
+ {
+ }
+ if (mt.isErrorAny())
+ {
+ System.err.println("Error when loading images!");
+ }
+ } while (!mt.checkAll());
Desktop.getInstance().setIconImage(logo);
}
} catch (Exception ex)
{
}
+
iframe = new JInternalFrame();
iframe.setFrameIcon(null);
- iframe.setClosable(isInteractive);
+ iframe.setClosable(true);
this.setLayout(new BorderLayout());
iframe.setContentPane(this);
iframe.setLayer(JLayeredPane.PALETTE_LAYER);
- SplashImage splashimg = new SplashImage(image);
- iconimg.add(splashimg, BorderLayout.CENTER);
- add(iconimg, BorderLayout.NORTH);
+ if (Platform.isJS())
+ {
+ // ignore in JavaScript
+ }
+ else
+ /**
+ * Java only
+ *
+ * @j2sIgnore
+ */
+ {
+ ((JTextPane) splashText).setEditable(false);
+
+ SplashImage splashimg = new SplashImage(image);
+ iconimg.add(splashimg, BorderLayout.CENTER);
+ add(iconimg, BorderLayout.NORTH);
+ }
+ add(splashText, BorderLayout.CENTER);
+ splashText.addMouseListener(closer);
Desktop.getDesktopPane().add(iframe);
refreshText();
}
- String oldtext;
-
- private int mainState;
-
/**
* update text in author text panel reflecting current version information
*/
protected boolean refreshText()
{
- Desktop desktop = Desktop.getInstance();
- String newtext = desktop.getAboutMessage(true).toString();
+ String newtext = Desktop.getInstance().getAboutMessage();
// System.err.println("Text found: \n"+newtext+"\nEnd of newtext.");
- if (!newtext.equals(oldtext))
+ if (oldTextLength != newtext.length())
{
iframe.setVisible(false);
- oldtext = newtext;
+ oldTextLength = newtext.length();
if (Platform.isJS()) // BH 2019
{
- // BH TODO SwingJS does not implement HTML style. Could rethink this.
-
- if (htmlPane == null)
- {
- htmlPane = new JLabel();
- }
- JLabel l = (JLabel)htmlPane;
- l.setText(newtext);
- Font f = htmlPane.getFont();
- l.setFont(new Font(f.getFamily(), Font.PLAIN, f.getSize()));
- l.setBorder(new EmptyBorder(new Insets(5, 5, 5, 5)));
- l.setOpaque(true);
- l.setBackground(Color.white);
- htmlPane = l;
+ /*
+ * SwingJS doesn't have HTMLEditorKit, required for a JTextPane
+ * to display formatted html, so we use a simple alternative
+ */
+ String text = "<html><br><br><img src=\"swingjs/j2s/images/Jalview_Logo.png\"/><br>"
+ + newtext + "</html>";
+ JLabel ta = new JLabel(text);
+ ta.setOpaque(true);
+ ta.setBackground(Color.white);
+ splashText = ta;
}
else
/**
* Java only
- *
+ *
* @j2sIgnore
*/
{
- if (htmlPane == null)
- {
- htmlPane = new JTextPane();
- }
- JTextPane pane = (JTextPane)htmlPane;
- pane.setEditable(false);
- pane.setContentType("text/html");
- pane.setText(newtext);
- pane.addHyperlinkListener(this);
- htmlPane = pane;
+ JTextPane jtp = new JTextPane();
+ jtp.setEditable(false);
+ jtp.setContentType("text/html");
+ jtp.setText("<html>" + newtext + "</html>");
+ jtp.addHyperlinkListener(this);
+ splashText = jtp;
}
- htmlPane.addMouseListener(closer);
- htmlPane.setSize(new Dimension(750, 375));
- add(htmlPane, BorderLayout.CENTER);
- int h = htmlPane.getHeight() + iconimg.getHeight();
- iframe.setBounds(Math.max(0, (desktop.getWidth() - 750) / 2),
- Math.max(0, (desktop.getHeight() - h) / 2), 750, h);
+ splashText.addMouseListener(closer);
+
+ splashText.setVisible(true);
+ splashText.setSize(new Dimension(750, 375));
+ add(splashText, BorderLayout.CENTER);
+ revalidate();
+ iframe.setBounds((Desktop.getInstance().getWidth() - 750) / 2,
+ (Desktop.getInstance().getHeight() - 375) / 2, 750,
+ splashText.getHeight() + iconimg.getHeight());
+ iframe.validate();
iframe.setVisible(true);
return true;
}
@Override
public void run()
{
- mainState = STATE_INIT;
- mainLoop();
- }
+ initSplashScreenWindow();
- protected long startTime;
+ long startTime = System.currentTimeMillis() / 1000;
- /**
- * A simple state machine with just three states: init, loop, and done. Ideal
- * for a simple while/sleep loop that works in Java and JavaScript
- * identically.
- *
- */
- protected void mainLoop()
- {
- while (true)
+ while (visible)
{
- switch (mainState)
+ iframe.repaint();
+ try
{
- case STATE_INIT:
- initSplashScreenWindow();
- startTime = System.currentTimeMillis() / 1000;
- mainState = STATE_LOOP;
- continue;
- case STATE_LOOP:
- if (!isVisible())
- {
- mainState = STATE_DONE;
- continue;
- }
- if (!isInteractive
- && ((System.currentTimeMillis() / 1000) - startTime) > 5)
- {
- setVisible(false);
- continue;
- }
- if (isVisible() && refreshText())
- {
- iframe.repaint();
- }
- if (isInteractive)
- {
- return;
- }
- Timer timer = new Timer(500, new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- mainLoop();
- }
+ Thread.sleep(500);
+ } catch (Exception ex)
+ {
+ }
- });
- timer.setRepeats(false);
- timer.start();
- return;
- case STATE_DONE:
- closeSplash();
- Desktop.getInstance().startDialogQueue();
+ if (transientDialog && ((System.currentTimeMillis() / 1000)
+ - startTime) > SHOW_FOR_SECS)
+ {
+ visible = false;
+ }
+
+ if (visible && refreshText())
+ {
+ iframe.repaint();
+ }
+ if (!transientDialog)
+ {
return;
}
}
+ closeSplash();
+ Desktop.getInstance().startDialogQueue();
}
/**
- * Close the internal frame, either from the timer expiring or from the mouse
- * action.
+ * DOCUMENT ME!
*/
public void closeSplash()
{
- setVisible(false);
try
{
+
iframe.setClosed(true);
- } catch (PropertyVetoException e)
+ } catch (Exception ex)
{
}
}
- private class SplashImage extends JPanel
+ public class SplashImage extends JPanel
{
- private Image image;
+ Image image;
public SplashImage(Image todisplay)
{
g.setColor(Color.white);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.black);
- g.setFont(largeFont);
+ g.setFont(new Font("Verdana", Font.BOLD, FONT_SIZE + 6));
if (image != null)
{
(getHeight() - image.getHeight(this)) / 2, this);
}
}
- /*
- * int y = yoffset;
- *
- * g.drawString("Jalview " + jalview.bin.Cache.getProperty("VERSION"), 50,
- * y);
- *
- * FontMetrics fm = g.getFontMetrics(); int vwidth =
- * fm.stringWidth("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
- * g.setFont(new Font("Verdana", Font.BOLD, fontSize + 2)); g.drawString(
- * "Last updated: " + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"),
- * 50 + vwidth + 5, y); if (jalview.bin.Cache.getDefault("LATEST_VERSION",
- * "Checking").equals( "Checking")) { // Displayed when code version and
- * jnlp version do not match g.drawString("...Checking latest version...",
- * 50, y += fontSize + 10); y += 5; g.setColor(Color.black); } else if
- * (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
- * .equals(jalview.bin.Cache.getProperty("VERSION"))) { if
- * (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
- * .indexOf("automated build") == -1) { // Displayed when code version and
- * jnlp version do not match and code // version is not a development build
- * g.setColor(Color.red); } g.drawString( "!! Jalview version " +
- * jalview.bin.Cache.getDefault("LATEST_VERSION", "..Checking..") +
- * " is available for download from "
- * +jalview.bin.Cache.getDefault("www.jalview.org"
- * ,"http://www.jalview.org")+" !!", 50, y += fontSize + 10); y += 5;
- * g.setColor(Color.black); }
- *
- * g.setFont(new Font("Verdana", Font.BOLD, fontSize)); g.drawString(
- * "Authors: Jim Procter, Andrew Waterhouse, Michele Clamp, James Cuff, Steve Searle,"
- * , 50, y += fontSize + 4); g.drawString("David Martin & Geoff Barton.",
- * 60, y += fontSize + 4); g.drawString(
- * "Development managed by The Barton Group, University of Dundee.", 50, y
- * += fontSize + 4); g.drawString("If you use Jalview, please cite: ", 50,
- * y += fontSize + 4); g.drawString(
- * "Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
- * , 50, y += fontSize + 4); g.drawString(
- * "Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
- * , 50, y += fontSize + 4);
- * g.drawString("Bioinformatics doi: 10.1093/bioinformatics/btp033", 50, y
- * += fontSize + 4); }
- */
}
@Override
Desktop.hyperlinkUpdate(e);
}
-
}
*/
package jalview.gui;
-import jalview.api.SplitContainerI;
-import jalview.datamodel.AlignmentI;
-import jalview.jbgui.GAlignFrame;
-import jalview.jbgui.GSplitFrame;
-import jalview.structure.StructureSelectionManager;
-import jalview.util.Platform;
-import jalview.viewmodel.AlignmentViewport;
-
+import java.awt.BorderLayout;
import java.awt.Component;
-import java.awt.Toolkit;
+import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import javax.swing.AbstractAction;
import javax.swing.InputMap;
+import javax.swing.JButton;
import javax.swing.JComponent;
+import javax.swing.JDesktopPane;
+import javax.swing.JInternalFrame;
+import javax.swing.JLayeredPane;
import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
+import jalview.api.AlignViewControllerGuiI;
+import jalview.api.FeatureSettingsControllerI;
+import jalview.api.SplitContainerI;
+import jalview.controller.FeatureSettingsControllerGuiI;
+import jalview.datamodel.AlignmentI;
+import jalview.jbgui.GAlignFrame;
+import jalview.jbgui.GSplitFrame;
+import jalview.structure.StructureSelectionManager;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.viewmodel.AlignmentViewport;
+
/**
* An internal frame on the desktop that hosts a horizontally split view of
* linked DNA and Protein alignments. Additional views can be created in linked
private static final long serialVersionUID = 1L;
+ /**
+ * geometry for Feature Settings Holder
+ */
+ private static final int FS_MIN_WIDTH = 400;
+
+ private static final int FS_MIN_HEIGHT = 400;
+
public SplitFrame(GAlignFrame top, GAlignFrame bottom)
{
super(top, bottom);
*/
public void adjustLayout()
{
+ final AlignViewport topViewport = ((AlignFrame) getTopFrame()).viewport;
+ final AlignViewport bottomViewport = ((AlignFrame) getBottomFrame()).viewport;
+
/*
* Ensure sequence ids are the same width so sequences line up
*/
- int w1 = ((AlignFrame) getTopFrame()).getViewport().getIdWidth();
- int w2 = ((AlignFrame) getBottomFrame()).getViewport().getIdWidth();
+ int w1 = topViewport.getIdWidth();
+ int w2 = bottomViewport.getIdWidth();
int w3 = Math.max(w1, w2);
- if (w1 != w3)
- {
- ((AlignFrame) getTopFrame()).getViewport().setIdWidth(w3);
- }
- if (w2 != w3)
- {
- ((AlignFrame) getBottomFrame()).getViewport().setIdWidth(w3);
- }
+ topViewport.setIdWidth(w3);
+ bottomViewport.setIdWidth(w3);
/*
* Scale protein to either 1 or 3 times character width of dna
*/
- final AlignViewport topViewport = ((AlignFrame) getTopFrame()).viewport;
- final AlignViewport bottomViewport = ((AlignFrame) getBottomFrame()).viewport;
final AlignmentI topAlignment = topViewport.getAlignment();
final AlignmentI bottomAlignment = bottomViewport.getAlignment();
AlignmentViewport cdna = topAlignment.isNucleotide() ? topViewport
* Ctrl-W / Cmd-W - close view or window
*/
KeyStroke key_cmdW = KeyStroke.getKeyStroke(KeyEvent.VK_W,
- Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+ jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
action = new AbstractAction()
{
@Override
* Ctrl-T / Cmd-T open new view
*/
KeyStroke key_cmdT = KeyStroke.getKeyStroke(KeyEvent.VK_T,
- Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+ jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
AbstractAction action = new AbstractAction()
{
@Override
{ (AlignFrame) getTopFrame(), (AlignFrame) getBottomFrame() });
}
+ @Override
+ public AlignFrame getComplementAlignFrame(
+ AlignViewControllerGuiI alignFrame)
+ {
+ if (getTopFrame() == alignFrame)
+ {
+ return (AlignFrame) getBottomFrame();
+ }
+ if (getBottomFrame() == alignFrame)
+ {
+ return (AlignFrame) getTopFrame();
+ }
+ // we didn't know anything about this frame...
+ return null;
+ }
+
/**
* Replace Cmd-F Find action with our version. This is necessary because the
* 'default' Finder searches in the first AlignFrame it finds. We need it to
* Ctrl-F / Cmd-F open Finder dialog, 'focused' on the right alignment
*/
KeyStroke key_cmdF = KeyStroke.getKeyStroke(KeyEvent.VK_F,
- Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+ jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
AbstractAction action = new AbstractAction()
{
@Override
};
overrideKeyBinding(key_cmdF, action);
}
+
+ /**
+ * Override to do nothing if triggered from one of the child frames
+ */
+ @Override
+ public void setSelected(boolean selected) throws PropertyVetoException
+ {
+ JDesktopPane desktopPane = getDesktopPane();
+ JInternalFrame fr = desktopPane == null ? null
+ : desktopPane.getSelectedFrame();
+ if (fr == getTopFrame() || fr == getBottomFrame())
+ {
+ /*
+ * patch for JAL-3288 (deselecting top/bottom frame closes popup menu);
+ * it may be possible to remove this method in future
+ * if the underlying Java behaviour changes
+ */
+ if (selected)
+ {
+ moveToFront();
+ }
+ return;
+ }
+ super.setSelected(selected);
+ }
+
+ /**
+ * holds the frame for feature settings, so Protein and DNA tabs can be managed
+ */
+ JInternalFrame featureSettingsUI;
+
+ JTabbedPane featureSettingsPanels;
+
+ @Override
+ public void addFeatureSettingsUI(
+ FeatureSettingsControllerGuiI featureSettings)
+ {
+ boolean showInternalFrame = false;
+ if (featureSettingsUI == null || featureSettingsPanels == null)
+ {
+ showInternalFrame = true;
+ featureSettingsPanels = new JTabbedPane();
+ featureSettingsPanels.addChangeListener(new ChangeListener()
+ {
+
+ @Override
+ public void stateChanged(ChangeEvent e)
+ {
+ if (e.getSource() != featureSettingsPanels
+ || featureSettingsUI == null
+ || featureSettingsUI.isClosed()
+ || !featureSettingsUI.isVisible())
+ {
+ // not our tabbed pane
+ return;
+ }
+ int tab = featureSettingsPanels.getSelectedIndex();
+ if (tab < 0 || featureSettingsPanels
+ .getSelectedComponent() instanceof FeatureSettingsControllerGuiI)
+ {
+ // no tab selected or already showing a feature settings GUI
+ return;
+ }
+ getAlignFrames().get(tab).showFeatureSettingsUI();
}
+ });
+ featureSettingsUI = new JInternalFrame(MessageManager.getString(
+ "label.sequence_feature_settings_for_CDS_and_Protein"));
+ featureSettingsPanels.setOpaque(true);
+
+ JPanel dialog = new JPanel();
+ dialog.setOpaque(true);
+ dialog.setLayout(new BorderLayout());
+ dialog.add(featureSettingsPanels, BorderLayout.CENTER);
+ JPanel buttons = new JPanel();
+ JButton ok = new JButton(MessageManager.getString("action.ok"));
+ ok.addActionListener(new ActionListener()
+ {
+
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ try
+ {
+ featureSettingsUI.setClosed(true);
+ } catch (PropertyVetoException pv)
+ {
+ pv.printStackTrace();
+ }
+ }
+ });
+ JButton cancel = new JButton(
+ MessageManager.getString("action.cancel"));
+ cancel.addActionListener(new ActionListener()
+ {
+
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ try
+ {
+ for (Component fspanel : featureSettingsPanels.getComponents())
+ {
+ if (fspanel instanceof FeatureSettingsControllerGuiI)
+ {
+ ((FeatureSettingsControllerGuiI) fspanel).revert();
+ }
+ }
+ featureSettingsUI.setClosed(true);
+ } catch (Exception pv)
+ {
+ pv.printStackTrace();
+ }
+ }
+ });
+ buttons.add(ok);
+ buttons.add(cancel);
+ dialog.add(buttons, BorderLayout.SOUTH);
+ featureSettingsUI.setContentPane(dialog);
+ createDummyTabs();
+ }
+ if (featureSettingsPanels
+ .indexOfTabComponent((Component) featureSettings) > -1)
+ {
+ // just show the feature settings !
+ featureSettingsPanels
+ .setSelectedComponent((Component) featureSettings);
+ return;
+ }
+ // otherwise replace the dummy tab with the given feature settings
+ int pos = getAlignFrames().indexOf(featureSettings.getAlignframe());
+ // if pos==-1 then alignFrame isn't managed by this splitframe
+ if (pos == 0)
+ {
+ featureSettingsPanels.removeTabAt(0);
+ featureSettingsPanels.insertTab(tabName[0], null,
+ (Component) featureSettings,
+ MessageManager.formatMessage(
+ "label.sequence_feature_settings_for", tabName[0]),
+ 0);
+ }
+ if (pos == 1)
+ {
+ featureSettingsPanels.removeTabAt(1);
+ featureSettingsPanels.insertTab(tabName[1], null,
+ (Component) featureSettings,
+ MessageManager.formatMessage(
+ "label.sequence_feature_settings_for", tabName[1]),
+ 1);
+ }
+ featureSettingsPanels.setSelectedComponent((Component) featureSettings);
+ // TODO: JAL-3535 - construct a feature settings title including names of
+ // currently selected CDS and Protein names
+
+ if (showInternalFrame)
+ {
+ if (Platform.isAMacAndNotJS())
+ {
+ Desktop.addInternalFrame(featureSettingsUI,
+ MessageManager.getString(
+ "label.sequence_feature_settings_for_CDS_and_Protein"),
+ 600, 480);
+ }
+ else
+ {
+ Desktop.addInternalFrame(featureSettingsUI,
+ MessageManager.getString(
+ "label.sequence_feature_settings_for_CDS_and_Protein"),
+ 600, 450);
+ }
+ featureSettingsUI
+ .setMinimumSize(new Dimension(FS_MIN_WIDTH, FS_MIN_HEIGHT));
+
+ featureSettingsUI.addInternalFrameListener(
+ new javax.swing.event.InternalFrameAdapter()
+ {
+ @Override
+ public void internalFrameClosed(
+ javax.swing.event.InternalFrameEvent evt)
+ {
+ for (int tab = 0; tab < featureSettingsPanels
+ .getTabCount();)
+ {
+ FeatureSettingsControllerGuiI fsettings = (FeatureSettingsControllerGuiI) featureSettingsPanels
+ .getTabComponentAt(tab);
+ if (fsettings != null)
+ {
+ featureSettingsPanels.removeTabAt(tab);
+ fsettings.featureSettings_isClosed();
+ }
+ else
+ {
+ tab++;
+ }
+ }
+ featureSettingsPanels = null;
+ featureSettingsUI = null;
+ };
+ });
+ featureSettingsUI.setLayer(JLayeredPane.PALETTE_LAYER);
+ }
+ }
+
+ /**
+ * tab names for feature settings
+ */
+ private String[] tabName = new String[] {
+ MessageManager.getString("label.CDS"),
+ MessageManager.getString("label.protein") };
+
+ /**
+ * create placeholder tabs which materialise the feature settings for a given
+ * view. Also reinitialises any tabs containing stale feature settings
+ */
+ private void createDummyTabs()
+ {
+ for (int tabIndex = 0; tabIndex < 2; tabIndex++)
+ {
+ JPanel dummyTab = new JPanel();
+ featureSettingsPanels.addTab(tabName[tabIndex], dummyTab);
+ }
+ }
+
+ private void replaceWithDummyTab(FeatureSettingsControllerI toClose)
+ {
+ Component dummyTab = null;
+ for (int tabIndex = 0; tabIndex < 2; tabIndex++)
+ {
+ if (featureSettingsPanels.getTabCount() > tabIndex)
+ {
+ dummyTab = featureSettingsPanels.getTabComponentAt(tabIndex);
+ if (dummyTab instanceof FeatureSettingsControllerGuiI
+ && !dummyTab.isVisible())
+ {
+ featureSettingsPanels.removeTabAt(tabIndex);
+ // close the feature Settings tab
+ ((FeatureSettingsControllerGuiI) dummyTab)
+ .featureSettings_isClosed();
+ // create a dummy tab in its place
+ dummyTab = new JPanel();
+ featureSettingsPanels.insertTab(tabName[tabIndex], null, dummyTab,
+ MessageManager.formatMessage(
+ "label.sequence_feature_settings_for",
+ tabName[tabIndex]),
+ tabIndex);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void closeFeatureSettings(
+ FeatureSettingsControllerI featureSettings,
+ boolean closeContainingFrame)
+ {
+ if (featureSettingsUI != null)
+ {
+ if (closeContainingFrame)
+ {
+ try
+ {
+ featureSettingsUI.setClosed(true);
+ } catch (Exception x)
+ {
+ }
+ featureSettingsUI = null;
+ }
+ else
+ {
+ replaceWithDummyTab(featureSettings);
+ }
+ }
+ }
+
+ @Override
+ public boolean isFeatureSettingsOpen()
+ {
+ return featureSettingsUI != null && !featureSettingsUI.isClosed();
+ }
+}
\ No newline at end of file
public void disableGui(boolean b)
{
- Desktop.getInstance().setVamsasUpdate(b);
+ // JAL-3311 TODO: remove this class!
+ // Desktop.instance.setVamsasUpdate(b);
}
Hashtable _backup_vobj2jv;
import jalview.datamodel.AlignedCodonFrame;
import jalview.datamodel.Alignment;
import jalview.datamodel.AlignmentI;
+import jalview.datamodel.MappedFeatures;
import jalview.datamodel.SequenceDummy;
import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceI;
import jalview.datamodel.features.FeatureMatcherSet;
import jalview.datamodel.features.FeatureMatcherSetI;
-import jalview.io.gff.GffHelperBase;
+import jalview.gui.Desktop;
import jalview.io.gff.GffHelperFactory;
import jalview.io.gff.GffHelperI;
import jalview.schemes.FeatureColour;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.TreeMap;
/**
* Parses and writes features files, which may be in Jalview, GFF2 or GFF3
*/
public class FeaturesFile extends AlignFile implements FeaturesSourceI
{
+ private static final String EQUALS = "=";
+
private static final String TAB_REGEX = "\\t";
private static final String STARTGROUP = "STARTGROUP";
float score = Float.NaN;
try
{
- score = new Float(gffColumns[6]).floatValue();
+ score = Float.valueOf(gffColumns[6]).floatValue();
} catch (NumberFormatException ex)
{
sf = new SequenceFeature(ft, desc, startPos, endPos, featureGroup);
}
/**
- * Returns contents of a Jalview format features file, for visible features,
- * as filtered by type and group. Features with a null group are displayed if
- * their feature type is visible. Non-positional features may optionally be
- * included (with no check on type or group).
+ * Returns contents of a Jalview format features file, for visible features, as
+ * filtered by type and group. Features with a null group are displayed if their
+ * feature type is visible. Non-positional features may optionally be included
+ * (with no check on type or group).
*
* @param sequences
* @param fr
* @param includeNonPositional
- * if true, include non-positional features (regardless of group or
- * type)
+ * if true, include non-positional features
+ * (regardless of group or type)
+ * @param includeComplement
+ * if true, include visible complementary
+ * (CDS/protein) positional features, with
+ * locations converted to local sequence
+ * coordinates
* @return
*/
public String printJalviewFormat(SequenceI[] sequences,
- FeatureRenderer fr, boolean includeNonPositional)
+ FeatureRenderer fr, boolean includeNonPositional,
+ boolean includeComplement)
{
Map<String, FeatureColourI> visibleColours = fr
.getDisplayedFeatureCols();
Map<String, FeatureMatcherSetI> featureFilters = fr.getFeatureFilters();
- if (!includeNonPositional
- && (visibleColours == null || visibleColours.isEmpty()))
- {
- // no point continuing.
- return "No Features Visible";
- }
+ // BH check this is out?
+// if (!includeNonPositional
+// && (visibleColours == null || visibleColours.isEmpty()))
+// {
+// // no point continuing.
+// return "No Features Visible";
+// }
/*
* write out feature colours (if we know them)
int count = outputFeaturesByGroup(out, fr, types, sequences,
includeNonPositional);
+ if (includeComplement)
+ {
+ count += outputComplementFeatures(out, fr, sequences);
+ }
+
return count > 0 ? out.toString() : "No Features Visible";
}
/**
+ * Outputs any visible complementary (CDS/peptide) positional features as
+ * Jalview format, within feature group. The coordinates of the linked features
+ * are converted to the corresponding positions of the local sequences.
+ *
+ * @param out
+ * @param fr
+ * @param sequences
+ * @return
+ */
+ private int outputComplementFeatures(StringBuilder out,
+ FeatureRenderer fr, SequenceI[] sequences)
+ {
+ AlignViewportI comp = fr.getViewport().getCodingComplement();
+ FeatureRenderer fr2 = Desktop.getAlignFrameFor(comp)
+ .getFeatureRenderer();
+
+ /*
+ * bin features by feature group and sequence
+ */
+ Map<String, Map<String, List<SequenceFeature>>> map = new TreeMap<>(
+ String.CASE_INSENSITIVE_ORDER);
+ int count = 0;
+
+ for (SequenceI seq : sequences)
+ {
+ /*
+ * find complementary features
+ */
+ List<SequenceFeature> complementary = findComplementaryFeatures(seq,
+ fr2);
+ String seqName = seq.getName();
+
+ for (SequenceFeature sf : complementary)
+ {
+ String group = sf.getFeatureGroup();
+ if (!map.containsKey(group))
+ {
+ map.put(group, new LinkedHashMap<>()); // preserves sequence order
+ }
+ Map<String, List<SequenceFeature>> groupFeatures = map.get(group);
+ if (!groupFeatures.containsKey(seqName))
+ {
+ groupFeatures.put(seqName, new ArrayList<>());
+ }
+ List<SequenceFeature> foundFeatures = groupFeatures.get(seqName);
+ foundFeatures.add(sf);
+ count++;
+ }
+ }
+
+ /*
+ * output features by group
+ */
+ for (Entry<String, Map<String, List<SequenceFeature>>> groupFeatures : map.entrySet())
+ {
+ out.append(newline);
+ String group = groupFeatures.getKey();
+ if (!"".equals(group))
+ {
+ out.append(STARTGROUP).append(TAB).append(group).append(newline);
+ }
+ Map<String, List<SequenceFeature>> seqFeaturesMap = groupFeatures
+ .getValue();
+ for (Entry<String, List<SequenceFeature>> seqFeatures : seqFeaturesMap
+ .entrySet())
+ {
+ String sequenceName = seqFeatures.getKey();
+ for (SequenceFeature sf : seqFeatures.getValue())
+ {
+ formatJalviewFeature(out, sequenceName, sf);
+ }
+ }
+ if (!"".equals(group))
+ {
+ out.append(ENDGROUP).append(TAB).append(group).append(newline);
+ }
+ }
+
+ return count;
+ }
+
+ /**
+ * Answers a list of mapped features visible in the (CDS/protein) complement,
+ * with feature positions translated to local sequence coordinates
+ *
+ * @param seq
+ * @param fr2
+ * @return
+ */
+ protected List<SequenceFeature> findComplementaryFeatures(SequenceI seq,
+ FeatureRenderer fr2)
+ {
+ /*
+ * avoid duplication of features (e.g. peptide feature
+ * at all 3 mapped codon positions)
+ */
+ List<SequenceFeature> found = new ArrayList<>();
+ List<SequenceFeature> complementary = new ArrayList<>();
+
+ for (int pos = seq.getStart(); pos <= seq.getEnd(); pos++)
+ {
+ MappedFeatures mf = fr2.findComplementFeaturesAtResidue(seq, pos);
+
+ if (mf != null)
+ {
+ MapList mapping = mf.mapping.getMap();
+ for (SequenceFeature sf : mf.features)
+ {
+ /*
+ * make a virtual feature with local coordinates
+ */
+ if (!found.contains(sf))
+ {
+ String group = sf.getFeatureGroup();
+ if (group == null)
+ {
+ group = "";
+ }
+ found.add(sf);
+ int begin = sf.getBegin();
+ int end = sf.getEnd();
+ int[] range = mf.mapping.getTo() == seq.getDatasetSequence()
+ ? mapping.locateInTo(begin, end)
+ : mapping.locateInFrom(begin, end);
+ SequenceFeature sf2 = new SequenceFeature(sf, range[0],
+ range[1], group, sf.getScore());
+ complementary.add(sf2);
+ }
+ }
+ }
+ }
+
+ return complementary;
+ }
+
+ /**
* Outputs any feature filters defined for visible feature types, sandwiched by
* STARTFILTERS and ENDFILTERS lines
*
}
}
firstInGroup = false;
- out.append(formatJalviewFeature(sequenceName, sf));
+ formatJalviewFeature(out, sequenceName, sf);
}
}
}
}
/**
+ * Formats one feature in Jalview format and appends to the string buffer
+ *
* @param out
* @param sequenceName
* @param sequenceFeature
*/
protected String formatJalviewFeature(
- String sequenceName, SequenceFeature sequenceFeature)
+ StringBuilder out, String sequenceName,
+ SequenceFeature sequenceFeature)
{
- StringBuilder out = new StringBuilder(64);
if (sequenceFeature.description == null
|| sequenceFeature.description.equals(""))
{
if (sequenceFeature.description.indexOf(href) == -1)
{
- out.append(" <a href=\"" + href + "\">" + label + "</a>");
+ out.append(" <a href=\"").append(href).append("\">")
+ .append(label).append("</a>");
}
}
* a map whose keys are the type names of visible features
* @param visibleFeatureGroups
* @param includeNonPositionalFeatures
+ * @param includeComplement
* @return
*/
public String printGffFormat(SequenceI[] sequences,
- FeatureRenderer fr, boolean includeNonPositionalFeatures)
+ FeatureRenderer fr, boolean includeNonPositionalFeatures,
+ boolean includeComplement)
+ {
+ FeatureRenderer fr2 = null;
+ if (includeComplement)
{
+ AlignViewportI comp = fr.getViewport().getCodingComplement();
+ fr2 = Desktop.getAlignFrameFor(comp).getFeatureRenderer();
+ }
+
Map<String, FeatureColourI> visibleColours = fr.getDisplayedFeatureCols();
StringBuilder out = new StringBuilder(256);
- out.append(String.format("%s %d" + newline, GFF_VERSION,
- gffVersion == 0 ? 2 : gffVersion));
-
- if (!includeNonPositionalFeatures
- && (visibleColours == null || visibleColours.isEmpty()))
- {
- return out.toString();
- }
+ out.append(String.format("%s %d\n", GFF_VERSION, gffVersion == 0 ? 2 : gffVersion));
String[] types = visibleColours == null ? new String[0]
: visibleColours.keySet()
for (SequenceI seq : sequences)
{
+ List<SequenceFeature> seqFeatures = new ArrayList<>();
List<SequenceFeature> features = new ArrayList<>();
if (includeNonPositionalFeatures)
{
for (SequenceFeature sf : features)
{
- if (!sf.isNonPositional() && !fr.isVisible(sf))
+ if (sf.isNonPositional() || fr.isVisible(sf))
{
/*
- * feature hidden by group visibility, colour threshold,
+ * drop features hidden by group visibility, colour threshold,
* or feature filter condition
*/
- continue;
+ seqFeatures.add(sf);
}
+ }
+
+ if (includeComplement)
+ {
+ seqFeatures.addAll(findComplementaryFeatures(seq, fr2));
+ }
+
+ /*
+ * sort features here if wanted
+ */
+ for (SequenceFeature sf : seqFeatures)
+ {
+ formatGffFeature(out, seq, sf);
+ out.append(newline);
+ }
+ }
+ return out.toString();
+ }
+
+ /**
+ * Formats one feature as GFF and appends to the string buffer
+ */
+ private void formatGffFeature(StringBuilder out, SequenceI seq,
+ SequenceFeature sf)
+ {
String source = sf.featureGroup;
if (source == null)
{
String phase = sf.getPhase();
out.append(phase == null ? "." : phase);
- // miscellaneous key-values (GFF column 9)
- String attributes = sf.getAttributes();
- if (attributes != null)
+ if (sf.otherDetails != null && !sf.otherDetails.isEmpty())
{
- out.append(TAB).append(attributes);
- }
+ Map<String, Object> map = sf.otherDetails;
+ formatAttributes(out, map);
+ }
+ }
- out.append(newline);
+ /**
+ * A helper method that outputs attributes stored in the map as
+ * semicolon-delimited values e.g.
+ *
+ * <pre>
+ * AC_Male=0;AF_NFE=0.00000e 00;Hom_FIN=0;GQ_MEDIAN=9
+ * </pre>
+ *
+ * A map-valued attribute is formatted as a comma-delimited list within braces,
+ * for example
+ *
+ * <pre>
+ * jvmap_CSQ={ALLELE_NUM=1,UNIPARC=UPI0002841053,Feature=ENST00000585561}
+ * </pre>
+ *
+ * The {@code jvmap_} prefix designates a values map and is removed if the value
+ * is parsed when read in. (The GFF3 specification allows 'semi-structured data'
+ * to be represented provided the attribute name begins with a lower case
+ * letter.)
+ *
+ * @param sb
+ * @param map
+ * @see http://gmod.org/wiki/GFF3#GFF3_Format
+ */
+ void formatAttributes(StringBuilder sb, Map<String, Object> map)
+ {
+ sb.append(TAB);
+ boolean first = true;
+ for (String key : map.keySet())
+ {
+ if (SequenceFeature.STRAND.equals(key)
+ || SequenceFeature.PHASE.equals(key))
+ {
+ /*
+ * values stashed in map but output to their own columns
+ */
+ continue;
+ }
+ {
+ if (!first)
+ {
+ sb.append(";");
+ }
+ }
+ first = false;
+ Object value = map.get(key);
+ if (value instanceof Map<?, ?>)
+ {
+ formatMapAttribute(sb, key, (Map<?, ?>) value);
+ }
+ else
+ {
+ String formatted = StringUtils.urlEncode(value.toString(),
+ GffHelperI.GFF_ENCODABLE);
+ sb.append(key).append(EQUALS).append(formatted);
}
}
+ }
- return out.toString();
+ /**
+ * Formats the map entries as
+ *
+ * <pre>
+ * key=key1=value1,key2=value2,...
+ * </pre>
+ *
+ * and appends this to the string buffer
+ *
+ * @param sb
+ * @param key
+ * @param map
+ */
+ private void formatMapAttribute(StringBuilder sb, String key,
+ Map<?, ?> map)
+ {
+ if (map == null || map.isEmpty())
+ {
+ return;
+ }
+
+ /*
+ * AbstractMap.toString would be a shortcut here, but more reliable
+ * to code the required format in case toString changes in future
+ */
+ sb.append(key).append(EQUALS);
+ boolean first = true;
+ for (Entry<?, ?> entry : map.entrySet())
+ {
+ if (!first)
+ {
+ sb.append(",");
+ }
+ first = false;
+ sb.append(entry.getKey().toString()).append(EQUALS);
+ String formatted = StringUtils.urlEncode(entry.getValue().toString(),
+ GffHelperI.GFF_ENCODABLE);
+ sb.append(formatted);
+ }
}
/**
return seq;
}
- /**
- * Process the 'column 9' data of the GFF file. This is less formally defined,
- * and its interpretation will vary depending on the tool that has generated
- * it.
- *
- * @param attributes
- * @param sf
- */
- protected void processGffColumnNine(String attributes, SequenceFeature sf)
- {
- sf.setAttributes(attributes);
-
- /*
- * Parse attributes in column 9 and add them to the sequence feature's
- * 'otherData' table; use Note as a best proxy for description
- */
- char nameValueSeparator = gffVersion == 3 ? '=' : ' ';
- // TODO check we don't break GFF2 values which include commas here
- Map<String, List<String>> nameValues = GffHelperBase
- .parseNameValuePairs(attributes, ";", nameValueSeparator, ",");
- for (Entry<String, List<String>> attr : nameValues.entrySet())
- {
- String values = StringUtils.listToDelimitedString(attr.getValue(),
- "; ");
- sf.setValue(attr.getKey(), values);
- if (NOTE.equals(attr.getKey()))
- {
- sf.setDescription(values);
- }
- }
- }
+ // BH! check that we did not lose something here.
+// /**
+// * Process the 'column 9' data of the GFF file. This is less formally defined,
+// * and its interpretation will vary depending on the tool that has generated
+// * it.
+// *
+// * @param attributes
+// * @param sf
+// */
+// protected void processGffColumnNine(String attributes, SequenceFeature sf)
+// {
+// sf.setAttributes(attributes);
+//
+// /*
+// * Parse attributes in column 9 and add them to the sequence feature's
+// * 'otherData' table; use Note as a best proxy for description
+// */
+// char nameValueSeparator = gffVersion == 3 ? '=' : ' ';
+// // TODO check we don't break GFF2 values which include commas here
+// Map<String, List<String>> nameValues = GffHelperBase
+// .parseNameValuePairs(attributes, ";", nameValueSeparator, ",");
+// for (Entry<String, List<String>> attr : nameValues.entrySet())
+// {
+// String values = StringUtils.listToDelimitedString(attr.getValue(),
+// "; ");
+// sf.setValue(attr.getKey(), values);
+// if (NOTE.equals(attr.getKey()))
+// {
+// sf.setDescription(values);
+// }
+// }
+// }
/**
* After encountering ##fasta in a GFF3 file, process the remainder of the
*/
private static int MAX_HISTORY = 11;
- private File selectedFile;
-
String file;
DataSourceType protocol;
AlignmentFileReaderI source; // alternative specification of where data
// comes from
+ AlignViewport viewport;
+
+ AlignFrame alignFrame;
+
+ long loadtime;
+
+ long memused;
+
+ boolean raiseGUI = true;
+
+ private File selectedFile;
+
+ /**
+ * default constructor always raised errors in GUI dialog boxes
+ */
+ public FileLoader()
+ {
+ this(true);
+ }
+
+ /**
+ * construct a Fileloader that may raise errors non-interactively
+ *
+ * @param raiseGUI
+ * true if errors are to be raised as GUI dialog boxes
+ */
+ public FileLoader(boolean raiseGUI)
+ {
+ this.raiseGUI = raiseGUI;
+ }
+
/**
* It is critical that all these fields are set, as this instance is reused.
*
this.format = format;
}
- AlignViewport viewport;
-
- AlignFrame alignFrame;
-
- long loadtime;
-
- long memused;
-
- boolean raiseGUI = true;
/**
- * default constructor always raised errors in GUI dialog boxes
+ * Uppercase LoadFile is deprecated because it does not pass byte[] data to
+ * JavaScript
+ *
+ * @param viewport
+ * @param file
+ * @param protocol
+ * @param format
*/
- public FileLoader()
+ @Deprecated
+ public void LoadFile(AlignViewport viewport, String file,
+ DataSourceType protocol, FileFormatI format)
{
- this(true);
+ if (viewport != null)
+ {
+ this.viewport = viewport;
+ }
+ loadFile(file, protocol, format);
+ }
+
+ public void LoadFile(AlignViewport viewport, File file,
+ DataSourceType protocol, FileFormatI format)
+ {
+ loadFile(viewport, file, protocol, format);
}
/**
- * construct a Fileloader that may raise errors non-interactively
+ * Uppercase LoadFile is deprecated because it does not pass byte[] data to
+ * JavaScript
*
- * @param raiseGUI
- * true if errors are to be raised as GUI dialog boxes
+ * @param file
+ * @param protocol
+ * @param format
*/
- public FileLoader(boolean raiseGUI)
+ @Deprecated
+ public void LoadFile(String file, DataSourceType protocol,
+ FileFormatI format)
{
- this.raiseGUI = raiseGUI;
+ loadFile(file, protocol, format);
}
- public void loadFile(AlignViewport viewport, Object file,
+ /**
+ * necessary to use Object here in order to pass the file data
+ *
+ * @param viewport
+ * @param file
+ * File preferably to String
+ * @param protocol
+ * @param format
+ */
+ public void loadFile(AlignViewport viewport, File file,
DataSourceType protocol, FileFormatI format)
{
- this.viewport = viewport;
- if (file instanceof File) {
- this.selectedFile = (File) file;
- file = selectedFile.getPath();
+ if (viewport != null)
+ {
+ this.viewport = viewport;
}
- loadFile(file.toString(), protocol, format);
+ this.selectedFile = file;
+ loadFile(selectedFile.getPath(), protocol, format);
}
- public void loadFile(String file, DataSourceType protocol,
+ private void loadFile(String file, DataSourceType protocol,
FileFormatI format)
{
this.file = file;
* @param sourceType
* @return alignFrame constructed from file contents
*/
+ @Deprecated
public AlignFrame LoadFileWaitTillLoaded(String file,
DataSourceType sourceType)
{
- return loadFileWaitTillLoaded(file, sourceType, null);
+ return LoadFileWaitTillLoaded(file, sourceType, null);
}
/**
* @param format
* @return alignFrame constructed from file contents
*/
- public AlignFrame loadFileWaitTillLoaded(String file,
+ @Deprecated
+ public AlignFrame LoadFileWaitTillLoaded(String file,
DataSourceType sourceType, FileFormatI format)
{
setFileFields(null, null, file, sourceType, format);
// refer to it as.
return;
}
+ // BH logic change here just includes ignoring file==null
if (file == null
|| file.indexOf(System.getProperty("java.io.tmpdir")) > -1)
{
String historyItems = Cache.getProperty(type);
+ // BH simpler coding
if (historyItems != null)
{
String[] tokens = historyItems.split("\\t");
import jalview.api.FeatureColourI;
import jalview.datamodel.DBRefEntry;
import jalview.datamodel.DBRefSource;
+import jalview.datamodel.GeneLociI;
+import jalview.datamodel.MappedFeatures;
import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceI;
import jalview.util.MessageManager;
*/
public class SequenceAnnotationReport
{
+ private static final int MAX_DESCRIPTION_LENGTH = 40;
+
private static final String COMMA = ",";
private static final String ELLIPSIS = "...";
@Override
public int compare(DBRefEntry ref1, DBRefEntry ref2)
{
- if (ref1.isChromosome())
+ if (ref1 instanceof GeneLociI)
{
return -1;
}
- if (ref2.isChromosome())
+ if (ref2 instanceof GeneLociI)
{
return 1;
}
}
/**
- * Append text for the list of features to the tooltip
+ * Append text for the list of features to the tooltip Returns number of
+ * features left if maxlength limit is (or would have been) reached
*
* @param sb
- * @param rpos
+ * @param residuePos
* @param features
* @param minmax
+ * @param maxlength
*/
- public void appendFeatures(final StringBuilder sb, int rpos,
+ public int appendFeaturesLengthLimit(final StringBuilder sb,
+ int residuePos, List<SequenceFeature> features,
+ FeatureRendererModel fr, int maxlength)
+ {
+ for (int i = 0; i < features.size(); i++)
+ {
+ SequenceFeature feature = features.get(i);
+ if (appendFeature(sb, residuePos, fr, feature, null, maxlength))
+ {
+ return features.size() - i;
+ }
+ }
+ return 0;
+ }
+
+ public void appendFeatures(final StringBuilder sb, int residuePos,
List<SequenceFeature> features, FeatureRendererModel fr)
{
- if (features != null)
+ appendFeaturesLengthLimit(sb, residuePos, features, fr, 0);
+ }
+
+ /**
+ * Appends text for mapped features (e.g. CDS feature for peptide or vice versa)
+ * Returns number of features left if maxlength limit is (or would have been)
+ * reached
+ *
+ * @param sb
+ * @param residuePos
+ * @param mf
+ * @param fr
+ * @param maxlength
+ */
+ public int appendFeaturesLengthLimit(StringBuilder sb, int residuePos,
+ MappedFeatures mf, FeatureRendererModel fr, int maxlength)
+ {
+ for (int i = 0; i < mf.features.size(); i++)
{
- for (SequenceFeature feature : features)
+ SequenceFeature feature = mf.features.get(i);
+ if (appendFeature(sb, residuePos, fr, feature, mf, maxlength))
{
- appendFeature(sb, rpos, fr, feature);
+ return mf.features.size() - i;
}
}
+ return 0;
+ }
+
+ public void appendFeatures(StringBuilder sb, int residuePos,
+ MappedFeatures mf, FeatureRendererModel fr)
+ {
+ appendFeaturesLengthLimit(sb, residuePos, mf, fr, 0);
}
/**
* @param minmax
* @param feature
*/
- void appendFeature(final StringBuilder sb, int rpos,
- FeatureRendererModel fr, SequenceFeature feature)
+ boolean appendFeature(final StringBuilder sb0, int rpos,
+ FeatureRendererModel fr, SequenceFeature feature,
+ MappedFeatures mf, int maxlength)
{
+ StringBuilder sb = new StringBuilder();
if (feature.isContactFeature())
{
if (feature.getBegin() == rpos || feature.getEnd() == rpos)
{
- if (sb.length() > 6)
+ if (sb0.length() > 6)
{
- sb.append("<br>");
+ sb.append("<br/>");
}
sb.append(feature.getType()).append(" ").append(feature.getBegin())
.append(":").append(feature.getEnd());
}
- return;
+ return appendTextMaxLengthReached(sb0, sb, maxlength);
}
- if (sb.length() > 6)
+ if (sb0.length() > 6)
{
- sb.append("<br>");
+ sb.append("<br/>");
}
// TODO: remove this hack to display link only features
boolean linkOnly = feature.getValue("linkonly") != null;
if (description != null && !description.equals(feature.getType()))
{
description = StringUtils.stripHtmlTags(description);
+
+ /*
+ * truncate overlong descriptions unless they contain an href
+ * before the truncation point (as truncation could leave corrupted html)
+ */
+ int linkindex = description.toLowerCase().indexOf("<a ");
+ boolean hasLink = linkindex > -1
+ && linkindex < MAX_DESCRIPTION_LENGTH;
+ if (description.length() > MAX_DESCRIPTION_LENGTH && !hasLink)
+ {
+ description = description.substring(0, MAX_DESCRIPTION_LENGTH)
+ + ELLIPSIS;
+ }
+
sb.append("; ").append(description);
}
}
}
}
+
+ if (mf != null)
+ {
+ String variants = mf.findProteinVariants(feature);
+ if (!variants.isEmpty())
+ {
+ sb.append(" ").append(variants);
+ }
+ }
+ }
+ return appendTextMaxLengthReached(sb0, sb, maxlength);
+ }
+
+ void appendFeature(final StringBuilder sb, int rpos,
+ FeatureRendererModel fr, SequenceFeature feature,
+ MappedFeatures mf)
+ {
+ appendFeature(sb, rpos, fr, feature, mf, 0);
+ }
+
+ private static boolean appendTextMaxLengthReached(StringBuilder sb0,
+ StringBuilder sb, int maxlength)
+ {
+ boolean ret = false;
+ if (maxlength == 0 || sb0.length() + sb.length() < maxlength)
+ {
+ sb0.append(sb);
+ return false;
+ } else {
+ return true;
}
}
+ (urllink.get(0).toLowerCase()
.equals(urllink.get(1).toLowerCase()) ? urllink
.get(0) : (urllink.get(0) + ":" + urllink
- .get(1))) + "</a></br>");
+ .get(1)))
+ + "</a><br/>");
}
} catch (Exception x)
{
if (sequence.getDescription() != null)
{
tmp = sequence.getDescription();
- sb.append("<br>").append(tmp);
+ sb.append(tmp);
maxWidth = Math.max(maxWidth, tmp.length());
}
SequenceI ds = sequence;
.getNonPositionalFeatures())
{
int sz = -sb.length();
- appendFeature(sb, 0, fr, sf);
+ appendFeature(sb, 0, fr, sf, null);
sz += sb.length();
maxWidth = Math.max(maxWidth, sz);
}
countForSource++;
if (countForSource == 1 || !summary)
{
- sb.append("<br>");
+ sb.append("<br/>");
}
if (countForSource <= MAX_REFS_PER_SOURCE || !summary)
{
}
if (moreSources)
{
- sb.append("<br>").append(source).append(COMMA).append(ELLIPSIS);
+ sb.append("<br/>").append(source).append(COMMA).append(ELLIPSIS);
}
if (ellipsis)
{
- sb.append("<br>(");
+ sb.append("<br/>(");
sb.append(MessageManager.getString("label.output_seq_details"));
sb.append(")");
}
*/
public static Map<String, List<String>> parseNameValuePairs(String text)
{
- // TODO: can a value include a comma? if so it will be broken by this
return parseNameValuePairs(text, ";", ' ', ",");
}
/**
- * Return ' ' as the name-value separator used in column 9 attributes.
- */
- @Override
- protected char getNameValueSeparator()
- {
- return ' ';
- }
-
- /**
* Default processing if not overridden is just to construct a sequence
* feature
*/
}
/**
- * Return '=' as the name-value separator used in column 9 attributes.
- */
- @Override
- protected char getNameValueSeparator()
- {
- return '=';
- }
-
- /**
* Modifies the default SequenceFeature in order to set the Target sequence id
* as the description
*/
desc = (String) sf.getValue(ID);
}
+ /*
+ * and decode comma, equals, semi-colon as required by GFF3 spec
+ */
+ desc = StringUtils.urlDecode(desc, GFF_ENCODABLE);
+
return desc;
}
}
*/
public abstract class GffHelperBase implements GffHelperI
{
- private static final String NOTE = "Note";
+ private static final String INVALID_GFF_ATTRIBUTE_FORMAT = "Invalid GFF attribute format: ";
+
+ protected static final String COMMA = ",";
+
+ protected static final String EQUALS = "=";
+
+ protected static final String NOTE = "Note";
/*
* GFF columns 1-9 (zero-indexed):
/**
* Parses the input line to a map of name / value(s) pairs. For example the
- * line <br>
+ * line
+ *
+ * <pre>
* Notes=Fe-S;Method=manual curation, prediction; source = Pfam; Notes = Metal
- * <br>
+ * </pre>
+ *
* if parsed with delimiter=";" and separators {' ', '='} <br>
* would return a map with { Notes={Fe=S, Metal}, Method={manual curation,
* prediction}, source={Pfam}} <br>
* name), or GFF3 format (which uses '=' as the name/value delimiter, and
* strictly does not allow repeat occurrences of the same name - but does
* allow a comma-separated list of values).
+ * <p>
+ * Returns a (possibly empty) map of lists of values by attribute name.
*
* @param text
* @param namesDelimiter
* the major delimiter between name-value pairs
* @param nameValueSeparator
- * one or more separators used between name and value
+ * separator used between name and value
* @param valuesDelimiter
* delimits a list of more than one value
- * @return the name-values map (which may be empty but never null)
+ * @return
*/
public static Map<String, List<String>> parseNameValuePairs(String text,
String namesDelimiter, char nameValueSeparator,
String valuesDelimiter)
{
- Map<String, List<String>> map = new HashMap<String, List<String>>();
+ Map<String, List<String>> map = new HashMap<>();
if (text == null || text.trim().length() == 0)
{
return map;
}
- for (String pair : text.trim().split(namesDelimiter))
+ /*
+ * split by major delimiter (; for GFF3)
+ */
+ for (String nameValuePair : text.trim().split(namesDelimiter))
{
- pair = pair.trim();
- if (pair.length() == 0)
+ nameValuePair = nameValuePair.trim();
+ if (nameValuePair.length() == 0)
{
continue;
}
- int sepPos = pair.indexOf(nameValueSeparator);
+ /*
+ * find name/value separator (= for GFF3)
+ */
+ int sepPos = nameValuePair.indexOf(nameValueSeparator);
if (sepPos == -1)
{
- // no name=value present
+ // no name=value found
continue;
}
- String key = pair.substring(0, sepPos).trim();
- String values = pair.substring(sepPos + 1).trim();
- if (values.length() > 0)
+ String name = nameValuePair.substring(0, sepPos).trim();
+ String values = nameValuePair.substring(sepPos + 1).trim();
+ if (values.isEmpty())
+ {
+ continue;
+ }
+
+ List<String> vals = map.get(name);
+ if (vals == null)
+ {
+ vals = new ArrayList<>();
+ map.put(name, vals);
+ }
+
+ /*
+ * if 'values' contains more name/value separators, parse as a map
+ * (nested sub-attribute values)
+ */
+ if (values.indexOf(nameValueSeparator) != -1)
+ {
+ vals.add(values);
+ }
+ else
{
- List<String> vals = map.get(key);
- if (vals == null)
- {
- vals = new ArrayList<String>();
- map.put(key, vals);
- }
for (String val : values.split(valuesDelimiter))
{
vals.add(val);
}
}
}
+
return map;
}
int end = Integer.parseInt(gff[END_COL]);
/*
- * default 'score' is 0 rather than Float.NaN as the latter currently
- * disables the 'graduated colour => colour by label' option
+ * default 'score' is 0 rather than Float.NaN - see JAL-2554
*/
float score = 0f;
try
if (attributes != null)
{
/*
- * save 'raw' column 9 to allow roundtrip output as input
- */
- sf.setAttributes(gff[ATTRIBUTES_COL]);
-
- /*
* Add attributes in column 9 to the sequence feature's
- * 'otherData' table; use Note as a best proxy for description
+ * 'otherData' table; use Note as a best proxy for description;
+ * decode any encoded comma, equals, semi-colon as per GFF3 spec
*/
for (Entry<String, List<String>> attr : attributes.entrySet())
{
- String values = StringUtils.listToDelimitedString(attr.getValue(),
- ",");
- sf.setValue(attr.getKey(), values);
- if (NOTE.equals(attr.getKey()))
+ String key = attr.getKey();
+ List<String> values = attr.getValue();
+ if (values.size() == 1 && values.get(0).contains(EQUALS))
+ {
+ /*
+ * 'value' is actually nested subattributes as x=a,y=b,z=c
+ */
+ Map<String, String> valueMap = parseAttributeMap(values.get(0));
+ sf.setValue(key, valueMap);
+ }
+ else
{
- sf.setDescription(values);
+ String csvValues = StringUtils.listToDelimitedString(values,
+ COMMA);
+ csvValues = StringUtils.urlDecode(csvValues, GFF_ENCODABLE);
+ sf.setValue(key, csvValues);
+ if (NOTE.equals(key))
+ {
+ sf.setDescription(csvValues);
+ }
}
}
}
}
/**
- * Returns the character used to separate attributes names from values in GFF
- * column 9. This is space for GFF2, '=' for GFF3.
+ * Parses a (GFF3 format) list of comma-separated key=value pairs into a Map
+ * of {@code key,
+ * value} <br>
+ * An input string like {@code a=b,c,d=e,f=g,h} is parsed to
+ *
+ * <pre>
+ * a = "b,c"
+ * d = "e"
+ * f = "g,h"
+ * </pre>
+ *
+ * @param s
*
* @return
*/
- protected abstract char getNameValueSeparator();
+ protected static Map<String, String> parseAttributeMap(String s)
+ {
+ Map<String, String> map = new HashMap<>();
+ String[] fields = s.split(EQUALS);
+
+ /*
+ * format validation
+ */
+ boolean valid = true;
+ if (fields.length < 2)
+ {
+ /*
+ * need at least A=B here
+ */
+ valid = false;
+ }
+ else if (fields[0].isEmpty() || fields[0].contains(COMMA))
+ {
+ /*
+ * A,B=C is not a valid start, nor is =C
+ */
+ valid = false;
+ }
+ else
+ {
+ for (int i = 1; i < fields.length - 1; i++)
+ {
+ if (fields[i].isEmpty() || !fields[i].contains(COMMA))
+ {
+ /*
+ * intermediate tokens must include value,name
+ */
+ valid = false;
+ }
+ }
+ }
+
+ if (!valid)
+ {
+ System.err.println(INVALID_GFF_ATTRIBUTE_FORMAT + s);
+ return map;
+ }
+
+ int i = 0;
+ while (i < fields.length - 1)
+ {
+ boolean lastPair = i == fields.length - 2;
+ String before = fields[i];
+ String after = fields[i + 1];
+
+ /*
+ * if 'key' looks like a,b,c then the last token is the
+ * key
+ */
+ String theKey = before.contains(COMMA)
+ ? before.substring(before.lastIndexOf(COMMA) + 1)
+ : before;
+
+ theKey = theKey.trim();
+ if (theKey.isEmpty())
+ {
+ System.err.println(INVALID_GFF_ATTRIBUTE_FORMAT + s);
+ map.clear();
+ return map;
+ }
+
+ /*
+ * if 'value' looks like a,b,c then all but the last token is the value,
+ * unless this is the last field (no more = to follow), in which case
+ * all of it makes up the value
+ */
+ String theValue = after.contains(COMMA) && !lastPair
+ ? after.substring(0, after.lastIndexOf(COMMA))
+ : after;
+ map.put(StringUtils.urlDecode(theKey, GFF_ENCODABLE),
+ StringUtils.urlDecode(theValue, GFF_ENCODABLE));
+ i += 1;
+ }
+
+ return map;
+ }
/**
* Returns any existing mapping held on the alignment between the given
*/
public interface GffHelperI
{
+ /*
+ * GFF3 spec requires comma, equals, semi-colon, tab, percent characters to be
+ * encoded as %2C, %3D, %3B, %09, %25 respectively within data values
+ * see https://github.com/The-Sequence-Ontology/Specifications/blob/master/gff3.md
+ */
+ final String GFF_ENCODABLE = ",=;\t%";
final String RENAME_TOKEN = "$RENAME_TO$";
{ "snRNA", "transcript" },
{ "miRNA", "transcript" },
{ "lincRNA", "transcript" },
+ { "lnc_RNA", "transcript" },
{ "rRNA", "transcript" },
{ "mRNA", "transcript" },
// there are many more sub-types of ncRNA...
package jalview.io.vcf;
-import jalview.analysis.AlignmentUtils;
import jalview.analysis.Dna;
import jalview.api.AlignViewControllerGuiI;
import jalview.bin.Cache;
import jalview.util.MapList;
import jalview.util.MappingUtils;
import jalview.util.MessageManager;
+import jalview.util.StringUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import htsjdk.samtools.SAMSequenceDictionary;
import htsjdk.samtools.SAMSequenceRecord;
import htsjdk.samtools.util.CloseableIterator;
+import htsjdk.tribble.TribbleException;
import htsjdk.variant.variantcontext.Allele;
import htsjdk.variant.variantcontext.VariantContext;
+import htsjdk.variant.vcf.VCFConstants;
import htsjdk.variant.vcf.VCFHeader;
import htsjdk.variant.vcf.VCFHeaderLine;
import htsjdk.variant.vcf.VCFHeaderLineCount;
*/
public class VCFLoader
{
+ private static final String VCF_ENCODABLE = ":;=%,";
+
+ /*
+ * Jalview feature attributes for VCF fixed column data
+ */
+ private static final String VCF_POS = "POS";
+
+ private static final String VCF_ID = "ID";
+
+ private static final String VCF_QUAL = "QUAL";
+
+ private static final String VCF_FILTER = "FILTER";
+
+ private static final String NO_VALUE = VCFConstants.MISSING_VALUE_v4; // '.'
+
+ private static final String DEFAULT_SPECIES = "homo_sapiens";
+
/**
* A class to model the mapping from sequence to VCF coordinates. Cases include
* <ul>
/*
* Lookup keys, and default values, for Preference entries that describe
- * patterns for VCF and VEP fields to capture
+ * patterns for VCF and VEP fields to capture
*/
private static final String VEP_FIELDS_PREF = "VEP_FIELDS";
private static final String DEFAULT_VEP_FIELDS = ".*";// "Allele,Consequence,IMPACT,SWISSPROT,SIFT,PolyPhen,CLIN_SIG";
/*
+ * Lookup keys, and default values, for Preference entries that give
+ * mappings from tokens in the 'reference' header to species or assembly
+ */
+ private static final String VCF_ASSEMBLY = "VCF_ASSEMBLY";
+
+ private static final String DEFAULT_VCF_ASSEMBLY = "assembly19=GRCh37,hs37=GRCh37,grch37=GRCh37,grch38=GRCh38";
+
+ private static final String VCF_SPECIES = "VCF_SPECIES"; // default is human
+
+ private static final String DEFAULT_REFERENCE = "grch37"; // fallback default is human GRCh37
+
+ /*
* keys to fields of VEP CSQ consequence data
* see https://www.ensembl.org/info/docs/tools/vep/vep_formats.html
*/
private static final String PIPE_REGEX = "\\|";
/*
- * key for Allele Frequency output by VEP
- * see http://www.ensembl.org/info/docs/tools/vep/vep_formats.html
- */
- private static final String ALLELE_FREQUENCY_KEY = "AF";
-
- /*
* delimiter that separates multiple consequence data blocks
*/
private static final String COMMA = ",";
private VCFHeader header;
/*
+ * species (as a valid Ensembl term) the VCF is for
+ */
+ private String vcfSpecies;
+
+ /*
+ * genome assembly version (as a valid Ensembl identifier) the VCF is for
+ */
+ private String vcfAssembly;
+
+ /*
* a Dictionary of contigs (if present) referenced in the VCF file
*/
private SAMSequenceDictionary dictionary;
*/
Map<Integer, String> vepFieldsOfInterest;
+ /*
+ * key:value for which rejected data has been seen
+ * (the error is logged only once for each combination)
+ */
+ private Set<String> badData;
+
/**
* Constructor given a VCF file
*
*/
public SequenceI loadVCFContig(String contig)
{
- String ref = header.getOtherHeaderLine(VCFHeader.REFERENCE_KEY)
- .getValue();
+ VCFHeaderLine headerLine = header.getOtherHeaderLine(VCFHeader.REFERENCE_KEY);
+ if (headerLine == null)
+ {
+ Cache.log.error("VCF reference header not found");
+ return null;
+ }
+ String ref = headerLine.getValue();
if (ref.startsWith("file://"))
{
ref = ref.substring(7);
}
+ setSpeciesAndAssembly(ref);
SequenceI seq = null;
File dbFile = new File(ref);
{
HtsContigDb db = new HtsContigDb("", dbFile);
seq = db.getSequenceProxy(contig);
- loadSequenceVCF(seq, ref);
+ loadSequenceVCF(seq);
db.close();
}
else
{
- System.err.println("VCF reference not found: " + ref);
+ Cache.log.error("VCF reference not found: " + ref);
}
return seq;
{
VCFHeaderLine ref = header
.getOtherHeaderLine(VCFHeader.REFERENCE_KEY);
- String vcfAssembly = ref.getValue();
+ String reference = ref == null ? null : ref.getValue();
+
+ setSpeciesAndAssembly(reference);
int varCount = 0;
int seqCount = 0;
*/
for (SequenceI seq : seqs)
{
- int added = loadSequenceVCF(seq, vcfAssembly);
+ int added = loadSequenceVCF(seq);
if (added > 0)
{
seqCount++;
}
/**
+ * Attempts to determine and save the species and genome assembly version to
+ * which the VCF data applies. This may be done by parsing the {@code reference}
+ * header line, configured in a property file, or (potentially) confirmed
+ * interactively by the user.
+ * <p>
+ * The saved values should be identifiers valid for Ensembl's REST service
+ * {@code map} endpoint, so they can be used (if necessary) to retrieve the
+ * mapping between VCF coordinates and sequence coordinates.
+ *
+ * @param reference
+ * @see https://rest.ensembl.org/documentation/info/assembly_map
+ * @see https://rest.ensembl.org/info/assembly/human?content-type=text/xml
+ * @see https://rest.ensembl.org/info/species?content-type=text/xml
+ */
+ protected void setSpeciesAndAssembly(String reference)
+ {
+ if (reference == null)
+ {
+ Cache.log.error("No VCF ##reference found, defaulting to "
+ + DEFAULT_REFERENCE + ":" + DEFAULT_SPECIES);
+ reference = DEFAULT_REFERENCE; // default to GRCh37 if not specified
+ }
+ reference = reference.toLowerCase();
+
+ /*
+ * for a non-human species, or other assembly identifier,
+ * specify as a Jalview property file entry e.g.
+ * VCF_ASSEMBLY = hs37=GRCh37,assembly19=GRCh37
+ * VCF_SPECIES = c_elegans=celegans
+ * to map a token in the reference header to a value
+ */
+ String prop = Cache.getDefault(VCF_ASSEMBLY, DEFAULT_VCF_ASSEMBLY);
+ for (String token : prop.split(","))
+ {
+ String[] tokens = token.split("=");
+ if (tokens.length == 2)
+ {
+ if (reference.contains(tokens[0].trim().toLowerCase()))
+ {
+ vcfAssembly = tokens[1].trim();
+ break;
+ }
+ }
+ }
+
+ vcfSpecies = DEFAULT_SPECIES;
+ prop = Cache.getProperty(VCF_SPECIES);
+ if (prop != null)
+ {
+ for (String token : prop.split(","))
+ {
+ String[] tokens = token.split("=");
+ if (tokens.length == 2)
+ {
+ if (reference.contains(tokens[0].trim().toLowerCase()))
+ {
+ vcfSpecies = tokens[1].trim();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /**
* Opens the VCF file and parses header data
*
* @param filePath
/*
* dna-to-peptide product mapping
*/
- AlignmentUtils.computeProteinFeatures(seq, mapTo, map);
+ // JAL-3187 render on the fly instead
+ // AlignmentUtils.computeProteinFeatures(seq, mapTo, map);
}
else
{
* and returns the number of variant features added
*
* @param seq
- * @param vcfAssembly
* @return
*/
- protected int loadSequenceVCF(SequenceI seq, String vcfAssembly)
+ protected int loadSequenceVCF(SequenceI seq)
{
- VCFMap vcfMap = getVcfMap(seq, vcfAssembly);
+ VCFMap vcfMap = getVcfMap(seq);
if (vcfMap == null)
{
return 0;
* Answers a map from sequence coordinates to VCF chromosome ranges
*
* @param seq
- * @param vcfAssembly
* @return
*/
- private VCFMap getVcfMap(SequenceI seq, String vcfAssembly)
+ private VCFMap getVcfMap(SequenceI seq)
{
/*
* simplest case: sequence has id and length matching a VCF contig
String species = seqCoords.getSpeciesId();
String chromosome = seqCoords.getChromosomeId();
String seqRef = seqCoords.getAssemblyId();
- MapList map = seqCoords.getMap();
+ MapList map = seqCoords.getMapping();
- if (!vcfSpeciesMatchesSequence(vcfAssembly, species))
+ // note this requires the configured species to match that
+ // returned with the Ensembl sequence; todo: support aliases?
+ if (!vcfSpecies.equalsIgnoreCase(species))
{
+ Cache.log.warn("No VCF loaded to " + seq.getName()
+ + " as species not matched");
return null;
}
- if (vcfAssemblyMatchesSequence(vcfAssembly, seqRef))
+ if (seqRef.equalsIgnoreCase(vcfAssembly))
{
return new VCFMap(chromosome, map);
}
- if (!"GRCh38".equalsIgnoreCase(seqRef) // Ensembl
- || !vcfAssembly.contains("Homo_sapiens_assembly19")) // gnomAD
- {
- return null;
- }
-
/*
- * map chromosomal coordinates from sequence to VCF if the VCF
- * data has a different reference assembly to the sequence
+ * VCF data has a different reference assembly to the sequence:
+ * query Ensembl to map chromosomal coordinates from sequence to VCF
*/
- // TODO generalise for cases other than GRCh38 -> GRCh37 !
- // - or get the user to choose in a dialog
-
List<int[]> toVcfRanges = new ArrayList<>();
List<int[]> fromSequenceRanges = new ArrayList<>();
- String toRef = "GRCh37";
for (int[] range : map.getToRanges())
{
}
int[] newRange = mapReferenceRange(range, chromosome, "human", seqRef,
- toRef);
+ vcfAssembly);
if (newRange == null)
{
Cache.log.error(
String.format("Failed to map %s:%s:%s:%d:%d to %s", species,
- chromosome, seqRef, range[0], range[1], toRef));
+ chromosome, seqRef, range[0], range[1],
+ vcfAssembly));
continue;
}
else
}
/**
- * Answers true if we determine that the VCF data uses the same reference
- * assembly as the sequence, else false
- *
- * @param vcfAssembly
- * @param seqRef
- * @return
- */
- private boolean vcfAssemblyMatchesSequence(String vcfAssembly,
- String seqRef)
- {
- // TODO improve on this stub, which handles gnomAD and
- // hopes for the best for other cases
-
- if ("GRCh38".equalsIgnoreCase(seqRef) // Ensembl
- && vcfAssembly.contains("Homo_sapiens_assembly19")) // gnomAD
- {
- return false;
- }
- return true;
- }
-
- /**
- * Answers true if the species inferred from the VCF reference identifier
- * matches that for the sequence
- *
- * @param vcfAssembly
- * @param speciesId
- * @return
- */
- boolean vcfSpeciesMatchesSequence(String vcfAssembly, String speciesId)
- {
- // PROBLEM 1
- // there are many aliases for species - how to equate one with another?
- // PROBLEM 2
- // VCF ##reference header is an unstructured URI - how to extract species?
- // perhaps check if ref includes any (Ensembl) alias of speciesId??
- // TODO ask the user to confirm this??
-
- if (vcfAssembly.contains("Homo_sapiens") // gnomAD exome data example
- && "HOMO_SAPIENS".equals(speciesId)) // Ensembl species id
- {
- return true;
- }
-
- if (vcfAssembly.contains("c_elegans") // VEP VCF response example
- && "CAENORHABDITIS_ELEGANS".equals(speciesId)) // Ensembl
- {
- return true;
- }
-
- // this is not a sustainable solution...
-
- return false;
- }
-
- /**
* Queries the VCF reader for any variants that overlap the mapped chromosome
* ranges of the sequence, and adds as variant features. Returns the number of
* overlapping variants found.
{
int vcfStart = Math.min(range[0], range[1]);
int vcfEnd = Math.max(range[0], range[1]);
- CloseableIterator<VariantContext> variants = reader
- .query(map.chromosome, vcfStart, vcfEnd);
- while (variants.hasNext())
+ try
{
- VariantContext variant = variants.next();
+ CloseableIterator<VariantContext> variants = reader
+ .query(map.chromosome, vcfStart, vcfEnd);
+ while (variants.hasNext())
+ {
+ VariantContext variant = variants.next();
- int[] featureRange = map.map.locateInFrom(variant.getStart(),
- variant.getEnd());
+ int[] featureRange = map.map.locateInFrom(variant.getStart(),
+ variant.getEnd());
- if (featureRange != null)
- {
- int featureStart = Math.min(featureRange[0], featureRange[1]);
- int featureEnd = Math.max(featureRange[0], featureRange[1]);
- count += addAlleleFeatures(seq, variant, featureStart, featureEnd,
- forwardStrand);
+ if (featureRange != null)
+ {
+ int featureStart = Math.min(featureRange[0], featureRange[1]);
+ int featureEnd = Math.max(featureRange[0], featureRange[1]);
+ count += addAlleleFeatures(seq, variant, featureStart,
+ featureEnd, forwardStrand);
+ }
}
+ variants.close();
+ } catch (TribbleException e)
+ {
+ /*
+ * RuntimeException throwable by htsjdk
+ */
+ String msg = String.format("Error reading VCF for %s:%d-%d: %s ",
+ map.chromosome, vcfStart, vcfEnd);
+ Cache.log.error(msg);
}
- variants.close();
}
return count;
featureEnd, FEATURE_GROUP_VCF);
sf.setSource(sourceId);
- sf.setValue(Gff3Helper.ALLELES, alleles);
+ /*
+ * save the derived alleles as a named attribute; this will be
+ * needed when Jalview computes derived peptide variants
+ */
+ addFeatureAttribute(sf, Gff3Helper.ALLELES, alleles);
+
+ /*
+ * add selected VCF fixed column data as feature attributes
+ */
+ addFeatureAttribute(sf, VCF_POS, String.valueOf(variant.getStart()));
+ addFeatureAttribute(sf, VCF_ID, variant.getID());
+ addFeatureAttribute(sf, VCF_QUAL,
+ String.valueOf(variant.getPhredScaledQual()));
+ addFeatureAttribute(sf, VCF_FILTER, getFilter(variant));
addAlleleProperties(variant, sf, altAlleleIndex, consequence);
}
/**
+ * Answers the VCF FILTER value for the variant - or an approximation to it.
+ * This field is either PASS, or a semi-colon separated list of filters not
+ * passed. htsjdk saves filters as a HashSet, so the order when reassembled into
+ * a list may be different.
+ *
+ * @param variant
+ * @return
+ */
+ String getFilter(VariantContext variant)
+ {
+ Set<String> filters = variant.getFilters();
+ if (filters.isEmpty())
+ {
+ return NO_VALUE;
+ }
+ Iterator<String> iterator = filters.iterator();
+ String first = iterator.next();
+ if (filters.size() == 1)
+ {
+ return first;
+ }
+
+ StringBuilder sb = new StringBuilder(first);
+ while (iterator.hasNext())
+ {
+ sb.append(";").append(iterator.next());
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Adds one feature attribute unless the value is null, empty or '.'
+ *
+ * @param sf
+ * @param key
+ * @param value
+ */
+ void addFeatureAttribute(SequenceFeature sf, String key, String value)
+ {
+ if (value != null && !value.isEmpty() && !NO_VALUE.equals(value))
+ {
+ sf.setValue(key, value);
+ }
+ }
+
+ /**
* Determines the Sequence Ontology term to use for the variant feature type in
* Jalview. The default is 'sequence_variant', but a more specific term is used
* if:
}
/*
- * filter out fields we don't want to capture
- */
- if (!vcfFieldsOfInterest.contains(key))
- {
- continue;
- }
-
- /*
* we extract values for other data which are allele-specific;
* these may be per alternate allele (INFO[key].Number = 'A')
* or per allele including reference (INFO[key].Number = 'R')
* take the index'th value
*/
String value = getAttributeValue(variant, key, index);
- if (value != null)
+ if (value != null && isValid(variant, key, value))
+ {
+ /*
+ * decode colon, semicolon, equals sign, percent sign, comma (only)
+ * as required by the VCF specification (para 1.2)
+ */
+ value = StringUtils.urlDecode(value, VCF_ENCODABLE);
+ addFeatureAttribute(sf, key, value);
+ }
+ }
+ }
+
+ /**
+ * Answers true for '.', null, or an empty value, or if the INFO type is String.
+ * If the INFO type is Integer or Float, answers false if the value is not in
+ * valid format.
+ *
+ * @param variant
+ * @param infoId
+ * @param value
+ * @return
+ */
+ protected boolean isValid(VariantContext variant, String infoId,
+ String value)
+ {
+ if (value == null || value.isEmpty() || NO_VALUE.equals(value))
+ {
+ return true;
+ }
+ VCFInfoHeaderLine infoHeader = header.getInfoHeaderLine(infoId);
+ if (infoHeader == null)
+ {
+ Cache.log.error("Field " + infoId + " has no INFO header");
+ return false;
+ }
+ VCFHeaderLineType infoType = infoHeader.getType();
+ try
+ {
+ if (infoType == VCFHeaderLineType.Integer)
{
- sf.setValue(key, value);
+ Integer.parseInt(value);
}
+ else if (infoType == VCFHeaderLineType.Float)
+ {
+ Float.parseFloat(value);
+ }
+ } catch (NumberFormatException e)
+ {
+ logInvalidValue(variant, infoId, value);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Logs an error message for malformed data; duplicate messages (same id and
+ * value) are not logged
+ *
+ * @param variant
+ * @param infoId
+ * @param value
+ */
+ private void logInvalidValue(VariantContext variant, String infoId,
+ String value)
+ {
+ if (badData == null)
+ {
+ badData = new HashSet<>();
+ }
+ String token = infoId + ":" + value;
+ if (!badData.contains(token))
+ {
+ badData.add(token);
+ Cache.log.error(String.format("Invalid VCF data at %s:%d %s=%s",
+ variant.getContig(), variant.getStart(), infoId, value));
}
}
String id = vepFieldsOfInterest.get(i);
if (id != null)
{
+ /*
+ * VCF spec requires encoding of special characters e.g. '='
+ * so decode them here before storing
+ */
+ field = StringUtils.urlDecode(field, VCF_ENCODABLE);
csqValues.put(id, field);
}
}
private jalview.datamodel.Mapping addMapping(Mapping m)
{
SequenceI dsto = null;
- // Mapping m = dr.getMapping();
+ // Mapping m = dr.getMap();
int fr[] = new int[m.getMapListFrom().size() * 2];
Iterator<MapListFrom> from = m.getMapListFrom().iterator();// enumerateMapListFrom();
for (int _i = 0; from.hasNext(); _i += 2)
// TODO remove this? never called on SequenceListener type
public void mouseOverSequence(SequenceI sequence, int index, int pos);
- public void highlightSequence(SearchResultsI results);
+ /**
+ * Highlights any position(s) represented by the search results and
+ * (optionally) returns an informative message about the position(s)
+ * higlighted
+ *
+ * @param results
+ * @return
+ */
+ public String highlightSequence(SearchResultsI results);
// TODO remove this? never called
public void updateColours(SequenceI sequence, int index);
import jalview.io.StructureFile;
import jalview.util.MappingUtils;
import jalview.util.MessageManager;
+import jalview.util.Platform;
import jalview.ws.sifts.SiftsClient;
import jalview.ws.sifts.SiftsException;
import jalview.ws.sifts.SiftsSettings;
* @param pdbResNum
* @param chain
* @param pdbfile
+ * @return
*/
- public void mouseOverStructure(int pdbResNum, String chain,
+ public String mouseOverStructure(int pdbResNum, String chain,
String pdbfile)
{
AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
List<AtomSpec> atoms = Collections.singletonList(atomSpec);
- mouseOverStructure(atoms);
+ return mouseOverStructure(atoms);
}
/**
*
* @param atoms
*/
- public void mouseOverStructure(List<AtomSpec> atoms)
+ public String mouseOverStructure(List<AtomSpec> atoms)
{
if (listeners == null)
{
// old or prematurely sent event
- return;
+ return null;
}
boolean hasSequenceListener = false;
for (int i = 0; i < listeners.size(); i++)
}
if (!hasSequenceListener)
{
- return;
+ return null;
}
SearchResultsI results = findAlignmentPositionsForStructurePositions(
atoms);
+ String result = null;
for (Object li : listeners)
{
if (li instanceof SequenceListener)
{
- ((SequenceListener) li).highlightSequence(results);
+ String s = ((SequenceListener) li).highlightSequence(results);
+ if (s != null)
+ {
+ result = s;
+ }
}
}
+ return result;
}
/**
StringBuilder sb = new StringBuilder(64);
for (StructureMapping sm : mappings)
{
- if (sm.pdbfile.equals(pdbfile) && seqs.contains(sm.sequence))
+ if (Platform.pathEquals(sm.pdbfile, pdbfile)
+ && seqs.contains(sm.sequence))
{
sb.append(sm.mappingDetails);
sb.append(NEWLINE);
}
/**
- * Reset this object to its initial state by removing all registered
- * listeners, codon mappings, PDB file mappings.
- *
- * Called only by Desktop and testng.
- *
+ * Resets this object to its initial state by removing all registered
+ * listeners, codon mappings, PDB file mappings
*/
public void resetAll()
{
- mappings.clear();
- seqmappings.clear();
- sel_listeners.clear();
- listeners.clear();
- commandListeners.clear();
- view_listeners.clear();
- pdbFileNameId.clear();
- pdbIdFileName.clear();
+ if (mappings != null)
+ {
+ mappings.clear();
+ }
+ if (seqmappings != null)
+ {
+ seqmappings.clear();
+ }
+ if (sel_listeners != null)
+ {
+ sel_listeners.clear();
+ }
+ if (listeners != null)
+ {
+ listeners.clear();
+ }
+ if (commandListeners != null)
+ {
+ commandListeners.clear();
+ }
+ if (view_listeners != null)
+ {
+ view_listeners.clear();
+ }
+ if (pdbFileNameId != null)
+ {
+ pdbFileNameId.clear();
+ }
+ if (pdbIdFileName != null)
+ {
+ pdbIdFileName.clear();
+ }
}
public void addSelectionListener(SelectionListener selecter)
return new MapList(getFromRanges(), toRanges, outFromRatio, outToRatio);
}
+ /**
+ * Answers true if the mapping is from one contiguous range to another, else
+ * false
+ *
+ * @return
+ */
+ public boolean isContiguous()
+ {
+ return fromShifts.size() == 1 && toShifts.size() == 1;
+ }
}
/**
*
* @return true if we are running in non-interactive no UI mode
+ * based on System.getProperty("java.awt.headless")
*/
public static boolean isHeadless()
{
return f.toString();
}
+ private static float javaVersion;
+
+ public static float getJavaVersion()
+ {
+ if (javaVersion == 0)
+ {
+ try
+ {
+ return javaVersion = Float.parseFloat(
+ System.getProperty("java.specification.version"));
+ } catch (Exception e)
+ {
+ javaVersion = 1.8f;
+ }
+ }
+ return javaVersion;
+ }
+
}
*/
package jalview.util;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
private static final Pattern DELIMITERS_PATTERN = Pattern
.compile(".*='[^']*(?!')");
+ private static final char PERCENT = '%';
+
private static final boolean DEBUG = false;
+ /*
+ * URL encoded characters, indexed by char value
+ * e.g. urlEncodings['='] = urlEncodings[61] = "%3D"
+ */
+ private static String[] urlEncodings = new String[255];
+
/**
* Returns a new character array, after inserting characters into the given
* character array.
}
/**
- * Answers true if the string is not empty and consists only of digits, or
- * characters 'a'-'f' or 'A'-'F', else false
+ * Answers the input string with any occurrences of the 'encodeable' characters
+ * replaced by their URL encoding
+ *
+ * @param s
+ * @param encodable
+ * @return
+ */
+ public static String urlEncode(String s, String encodable)
+ {
+ if (s == null || s.isEmpty())
+ {
+ return s;
+ }
+
+ /*
+ * do % encoding first, as otherwise it may double-encode!
+ */
+ if (encodable.indexOf(PERCENT) != -1)
+ {
+ s = urlEncode(s, PERCENT);
+ }
+
+ for (char c : encodable.toCharArray())
+ {
+ if (c != PERCENT)
+ {
+ s = urlEncode(s, c);
+ }
+ }
+ return s;
+ }
+
+ /**
+ * Answers the input string with any occurrences of {@code c} replaced with
+ * their url encoding. Answers the input string if it is unchanged.
+ *
+ * @param s
+ * @param c
+ * @return
+ */
+ static String urlEncode(String s, char c)
+ {
+ String decoded = String.valueOf(c);
+ if (s.indexOf(decoded) != -1)
+ {
+ String encoded = getUrlEncoding(c);
+ if (!encoded.equals(decoded))
+ {
+ s = s.replace(decoded, encoded);
+ }
+ }
+ return s;
+ }
+
+ /**
+ * Answers the input string with any occurrences of the specified (unencoded)
+ * characters replaced by their URL decoding.
+ * <p>
+ * Example: {@code urlDecode("a%3Db%3Bc", "-;=,")} should answer
+ * {@code "a=b;c"}.
*
* @param s
* @return
}
return true;
}
+ /**
+ * Answers the input string with any occurrences of the specified (unencoded)
+ * characters replaced by their URL decoding.
+ * <p>
+ * Example: {@code urlDecode("a%3Db%3Bc", "-;=,")} should answer
+ * {@code "a=b;c"}.
+ *
+ * @param s
+ * @param encodable
+ * @return
+ */
+ public static String urlDecode(String s, String encodable)
+ {
+ if (s == null || s.isEmpty())
+ {
+ return s;
+ }
+
+ for (char c : encodable.toCharArray())
+ {
+ String encoded = getUrlEncoding(c);
+ if (s.indexOf(encoded) != -1)
+ {
+ String decoded = String.valueOf(c);
+ s = s.replace(encoded, decoded);
+ }
+ }
+ return s;
+ }
+
+ /**
+ * Does a lazy lookup of the url encoding of the given character, saving the
+ * value for repeat lookups
+ *
+ * @param c
+ * @return
+ */
+ private static String getUrlEncoding(char c)
+ {
+ if (c < 0 || c >= urlEncodings.length)
+ {
+ return String.valueOf(c);
+ }
+
+ String enc = urlEncodings[c];
+ if (enc == null)
+ {
+ try
+ {
+ enc = urlEncodings[c] = URLEncoder.encode(String.valueOf(c),
+ "UTF-8");
+ } catch (UnsupportedEncodingException e)
+ {
+ enc = urlEncodings[c] = String.valueOf(c);
+ }
+ }
+ return enc;
+ }
+
+
}
viewStyle.setProteinFontAsCdna(b);
}
+ @Override
+ public void setShowComplementFeatures(boolean b)
+ {
+ viewStyle.setShowComplementFeatures(b);
+ }
+
+ @Override
+ public boolean isShowComplementFeatures()
+ {
+ return viewStyle.isShowComplementFeatures();
+ }
+
+ @Override
+ public void setShowComplementFeaturesOnTop(boolean b)
+ {
+ viewStyle.setShowComplementFeaturesOnTop(b);
+ }
+
+ @Override
+ public boolean isShowComplementFeaturesOnTop()
+ {
+ return viewStyle.isShowComplementFeaturesOnTop();
+ }
+
/**
* @return true if view should scroll to show the highlighted region of a
* sequence
*/
package jalview.viewmodel.seqfeatures;
-import jalview.api.AlignViewportI;
-import jalview.api.FeatureColourI;
-import jalview.api.FeaturesDisplayedI;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceI;
-import jalview.datamodel.features.FeatureMatcherSetI;
-import jalview.datamodel.features.SequenceFeatures;
-import jalview.renderer.seqfeatures.FeatureRenderer;
-import jalview.schemes.FeatureColour;
-import jalview.util.ColorUtils;
-
import java.awt.Color;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import jalview.api.AlignViewportI;
+import jalview.api.FeatureColourI;
+import jalview.api.FeaturesDisplayedI;
+import jalview.datamodel.AlignedCodonFrame;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.MappedFeatures;
+import jalview.datamodel.Mapping;
+import jalview.datamodel.SearchResultMatchI;
+import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.FeatureMatcherSetI;
+import jalview.datamodel.features.SequenceFeatures;
+import jalview.renderer.seqfeatures.FeatureRenderer;
+import jalview.schemes.FeatureColour;
+import jalview.util.ColorUtils;
+import jalview.util.Platform;
+
public abstract class FeatureRendererModel
implements jalview.api.FeatureRenderer
{
* include features unless their feature group is not displayed, or
* they are hidden (have no colour) based on a filter or colour threshold
*/
+
+ // BH! check -- !featureGroupNotShown(sf) is from applet branch.
for (SequenceFeature sf : features)
{
if (!featureGroupNotShown(sf) && getColour(sf) != null)
{
featureOrder = new Hashtable<>();
}
- featureOrder.put(type, new Float(position));
+ featureOrder.put(type, Float.valueOf(position));
return position;
}
}
if (newGroupsVisible)
{
- featureGroups.put(group, new Boolean(true));
+ featureGroups.put(group, Boolean.valueOf(true));
return true;
}
return false;
@Override
public void setGroupVisibility(String group, boolean visible)
{
- featureGroups.put(group, new Boolean(visible));
+ featureGroups.put(group, Boolean.valueOf(visible));
}
@Override
for (String gst : toset)
{
Boolean st = featureGroups.get(gst);
- featureGroups.put(gst, new Boolean(visible));
+ featureGroups.put(gst, Boolean.valueOf(visible));
if (st != null)
{
rdrw = rdrw || (visible != st.booleanValue());
* @param sequenceFeature
* @return
*/
- protected boolean featureGroupNotShown(final SequenceFeature sequenceFeature)
+ public boolean featureGroupNotShown(final SequenceFeature sequenceFeature)
{
- Boolean b;
return featureGroups != null
&& sequenceFeature.featureGroup != null
- && sequenceFeature.featureGroup.length() > 0
- && (b = featureGroups.get(sequenceFeature.featureGroup)) != null
- && !b.booleanValue();
+ && sequenceFeature.featureGroup.length() != 0
+ && featureGroups.containsKey(sequenceFeature.featureGroup)
+ && !featureGroups.get(sequenceFeature.featureGroup)
+ .booleanValue();
}
/**
*/
@Override
public List<SequenceFeature> findFeaturesAtResidue(SequenceI sequence,
- int resNo)
+ int fromResNo, int toResNo)
{
List<SequenceFeature> result = new ArrayList<>();
if (!av.areFeaturesDisplayed() || getFeaturesDisplayed() == null)
* displayed, and feature group is null or the empty string
* or marked for display
*/
- Set<String> visibleFeatures = getFeaturesDisplayed()
- .getVisibleFeatures();
+ List<String> visibleFeatures = getDisplayedFeatureTypes();
String[] visibleTypes = visibleFeatures
.toArray(new String[visibleFeatures.size()]);
List<SequenceFeature> features = sequence.getFeatures().findFeatures(
- resNo, resNo, visibleTypes);
+ fromResNo, toResNo, visibleTypes);
for (SequenceFeature sf : features)
{
*/
public void filterFeaturesForDisplay(List<SequenceFeature> features)
{
+// BH! check -- what was the problem here? How is JalviewJS's IntervalStore different from
+ // other IntervalStore?
+ /*
+ * fudge: JalviewJS's IntervalStore lacks the sort method called :-(
+ */
+ if (Platform.isJS())
+ {
+ return;
+ }
+
/*
* don't remove 'redundant' features if
* - transparency is applied (feature count affects depth of feature colour)
}
@Override
+ public MappedFeatures findComplementFeaturesAtResidue(SequenceI sequence,
+ int pos)
+ {
+ SequenceI ds = sequence.getDatasetSequence();
+ if (ds == null)
+ {
+ ds = sequence;
+ }
+ final char residue = ds.getCharAt(pos - ds.getStart());
+
+ List<SequenceFeature> found = new ArrayList<>();
+ List<AlignedCodonFrame> mappings = this.av.getAlignment()
+ .getCodonFrame(sequence);
+
+ /*
+ * fudge: if no mapping found, check the complementary alignment
+ * todo: only store in one place? StructureSelectionManager?
+ */
+ if (mappings.isEmpty())
+ {
+ mappings = this.av.getCodingComplement().getAlignment()
+ .getCodonFrame(sequence);
+ }
+
+ /*
+ * todo: direct lookup of CDS for peptide and vice-versa; for now,
+ * have to search through an unordered list of mappings for a candidate
+ */
+ Mapping mapping = null;
+ SequenceI mapFrom = null;
+
+ for (AlignedCodonFrame acf : mappings)
+ {
+ mapping = acf.getMappingForSequence(sequence);
+ if (mapping == null || !mapping.getMap().isTripletMap())
+ {
+ continue; // we are only looking for 3:1 or 1:3 mappings
+ }
+ SearchResultsI sr = new SearchResults();
+ acf.markMappedRegion(ds, pos, sr);
+ for (SearchResultMatchI match : sr.getResults())
+ {
+ int fromRes = match.getStart();
+ int toRes = match.getEnd();
+ mapFrom = match.getSequence();
+ List<SequenceFeature> fs = findFeaturesAtResidue(
+ mapFrom, fromRes, toRes);
+ for (SequenceFeature sf : fs)
+ {
+ if (!found.contains(sf))
+ {
+ found.add(sf);
+ }
+ }
+ }
+
+ /*
+ * just take the first mapped features we find
+ */
+ if (!found.isEmpty())
+ {
+ break;
+ }
+ }
+ if (found.isEmpty())
+ {
+ return null;
+ }
+
+ /*
+ * sort by renderorder, inefficiently
+ */
+ List<SequenceFeature> result = new ArrayList<>();
+ for (String type : renderOrder)
+ {
+ for (SequenceFeature sf : found)
+ {
+ if (type.equals(sf.getType()))
+ {
+ result.add(sf);
+ if (result.size() == found.size())
+ {
+ return new MappedFeatures(mapping, mapFrom, pos, residue,
+ result);
+ }
+ }
+ }
+ }
+
+ return new MappedFeatures(mapping, mapFrom, pos, residue, result);
+ }
+
+ @Override
public boolean isVisible(SequenceFeature feature)
{
if (feature == null)
setShowNPFeats(vs.isShowNPFeats());
setShowSequenceFeaturesHeight(vs.isShowSequenceFeaturesHeight());
setShowSequenceFeatures(vs.isShowSequenceFeatures());
+ setShowComplementFeatures(vs.isShowComplementFeatures());
+ setShowComplementFeaturesOnTop(vs.isShowComplementFeaturesOnTop());
setShowText(vs.getShowText());
setShowUnconserved(vs.getShowUnconserved());
setTextColour(vs.getTextColour());
&& isShowSequenceFeaturesHeight() == vs
.isShowSequenceFeaturesHeight()
&& isShowSequenceFeatures() == vs.isShowSequenceFeatures()
+ && isShowComplementFeatures() == vs.isShowComplementFeatures()
+ && isShowComplementFeaturesOnTop() == vs
+ .isShowComplementFeaturesOnTop()
&& getShowText() == vs.getShowText()
&& getShowUnconserved() == vs.getShowUnconserved()
&& getThreshold() == vs.getThreshold()
private int fontStyle;
+ private boolean showComplementFeatures;
+
+ private boolean showComplementFeaturesOnTop;
+
/**
* GUI state
*
{
proteinFontAsCdna = b;
}
+
+ @Override
+ public void setShowComplementFeatures(boolean b)
+ {
+ showComplementFeatures = b;
+ }
+
+ @Override
+ public boolean isShowComplementFeatures()
+ {
+ return showComplementFeatures;
+ }
+
+ @Override
+ public void setShowComplementFeaturesOnTop(boolean b)
+ {
+ showComplementFeaturesOnTop = b;
+ }
+
+ @Override
+ public boolean isShowComplementFeaturesOnTop()
+ {
+ return showComplementFeaturesOnTop;
+ }
}
package jalview.ws;
import jalview.analysis.AlignSeq;
+import jalview.api.FeatureSettingsModelI;
import jalview.bin.Cache;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.DBRefEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
+import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
+import java.util.Map;
import java.util.StringTokenizer;
import java.util.Vector;
Arrays.asList(dataset));
List<String> warningMessages = new ArrayList<>();
+ // clear any old feature display settings recorded from past sessions
+ featureDisplaySettings = null;
+
int db = 0;
while (sdataset.size() > 0 && db < dbSources.length)
{
}
if (retrieved != null)
{
- transferReferences(sdataset, dbsource.getDbSource(), retrieved,
+ transferReferences(sdataset, dbsource, retrieved,
trimDsSeqs, warningMessages);
}
}
* @param warningMessages
* a list of messages to add to
*/
- boolean transferReferences(Vector<SequenceI> sdataset, String dbSource,
+ boolean transferReferences(Vector<SequenceI> sdataset,
+ DbSourceProxy dbSourceProxy,
AlignmentI retrievedAl, boolean trimDatasetSeqs,
List<String> warningMessages)
{
return false;
}
+ String dbSource = dbSourceProxy.getDbName();
boolean modified = false;
SequenceI[] retrieved = recoverDbSequences(
retrievedAl.getSequencesArray());
* seqs.elementAt(jj); if (!sequenceMatches.contains(sequence)) {
* sequenceMatches.addElement(sequence); } } } }
*/
+ if (sequenceMatches.size() > 0)
+ {
+ addFeatureSettings(dbSourceProxy);
+ }
// sequenceMatches now contains the set of all sequences associated with
// the returned db record
final String retrievedSeqString = retrievedSeq.getSequenceAsString();
return modified;
}
+ Map<String, FeatureSettingsModelI> featureDisplaySettings = null;
+
+ private void addFeatureSettings(DbSourceProxy dbSourceProxy)
+ {
+ FeatureSettingsModelI fsettings = dbSourceProxy
+ .getFeatureColourScheme();
+ if (fsettings != null)
+ {
+ if (featureDisplaySettings == null)
+ {
+ featureDisplaySettings = new HashMap<>();
+ }
+ featureDisplaySettings.put(dbSourceProxy.getDbName(), fsettings);
+ }
+ }
+
+ /**
+ *
+ * @return any feature settings associated with sources that have provided sequences
+ */
+ public List<FeatureSettingsModelI>getFeatureSettingsModels()
+ {
+ return featureDisplaySettings == null
+ ? Arrays.asList(new FeatureSettingsModelI[0])
+ : Arrays.asList(featureDisplaySettings.values()
+ .toArray(new FeatureSettingsModelI[1]));
+ }
/**
* Adds the message to the list unless it already contains it
*
{
int n;
if (sequencesArray == null || (n = sequencesArray.length) == 0)
- {
- return sequencesArray;
- }
+ return sequencesArray;
ArrayList<SequenceI> nseq = new ArrayList<>();
for (int i = 0;i < n; i++)
{
import jalview.xml.binding.embl.EntryType;
import jalview.xml.binding.embl.EntryType.Feature;
import jalview.xml.binding.embl.EntryType.Feature.Qualifier;
+import jalview.xml.binding.jalview.JalviewModel;
import jalview.xml.binding.embl.ROOT;
import jalview.xml.binding.embl.XrefType;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.regex.Pattern;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
*/
private static final String EMBL_NOT_FOUND_REPLY = "ERROR 12 No entries found.";
- private static final Pattern SPACE_PATTERN = Pattern.compile(" ");
-
public EmblXmlSource()
{
super();
try
{
- // BH 2019.10.06 note: exo-location of "X53828.1:60..1058" causes
- // RemoteFormatTest to fail at this point.
List<int[]> ranges = DnaUtils.parseLocation(location);
return listToArray(ranges);
} catch (ParseException e)
SequenceFeature sf = new SequenceFeature(type, desc, begin, end, group);
if (!vals.isEmpty())
{
- StringBuilder sb = new StringBuilder();
- boolean first = true;
for (Entry<String, String> val : vals.entrySet())
{
- if (!first)
- {
- sb.append(";");
- }
- sb.append(val.getKey()).append("=").append(val.getValue());
- first = false;
sf.setValue(val.getKey(), val.getValue());
}
- sf.setAttributes(sb.toString());
}
return sf;
}
-20200408084722
+20200409124432
-20200408084722
+20200409124432
assertTrue(AlignmentUtils.haveCrossRef(seq2, seq1));
// now the other way round
- seq1.setDBRefs(null);
+ seq1.setDBRefs(null);
seq2.addDBRef(new DBRefEntry("EMBL", "1", "A12345"));
assertTrue(AlignmentUtils.haveCrossRef(seq1, seq2));
assertTrue(AlignmentUtils.haveCrossRef(seq2, seq1));
}
/**
- * Test the method that computes a map of codon variants for each protein
- * position from "sequence_variant" features on dna
- */
- @Test(groups = "Functional")
- public void testBuildDnaVariantsMap()
- {
- SequenceI dna = new Sequence("dna", "atgAAATTTGGGCCCtag");
- MapList map = new MapList(new int[] { 1, 18 }, new int[] { 1, 5 }, 3, 1);
-
- /*
- * first with no variants on dna
- */
- LinkedHashMap<Integer, List<DnaVariant>[]> variantsMap = AlignmentUtils
- .buildDnaVariantsMap(dna, map);
- assertTrue(variantsMap.isEmpty());
-
- /*
- * single allele codon 1, on base 1
- */
- SequenceFeature sf1 = new SequenceFeature("sequence_variant", "", 1, 1,
- 0f, null);
- sf1.setValue("alleles", "T");
- sf1.setValue("ID", "sequence_variant:rs758803211");
- dna.addSequenceFeature(sf1);
-
- /*
- * two alleles codon 2, on bases 2 and 3 (distinct variants)
- */
- SequenceFeature sf2 = new SequenceFeature("sequence_variant", "", 5, 5,
- 0f, null);
- sf2.setValue("alleles", "T");
- sf2.setValue("ID", "sequence_variant:rs758803212");
- dna.addSequenceFeature(sf2);
- SequenceFeature sf3 = new SequenceFeature("sequence_variant", "", 6, 6,
- 0f, null);
- sf3.setValue("alleles", "G");
- sf3.setValue("ID", "sequence_variant:rs758803213");
- dna.addSequenceFeature(sf3);
-
- /*
- * two alleles codon 3, both on base 2 (one variant)
- */
- SequenceFeature sf4 = new SequenceFeature("sequence_variant", "", 8, 8,
- 0f, null);
- sf4.setValue("alleles", "C, G");
- sf4.setValue("ID", "sequence_variant:rs758803214");
- dna.addSequenceFeature(sf4);
-
- // no alleles on codon 4
-
- /*
- * alleles on codon 5 on all 3 bases (distinct variants)
- */
- SequenceFeature sf5 = new SequenceFeature("sequence_variant", "", 13,
- 13, 0f, null);
- sf5.setValue("alleles", "C, G"); // (C duplicates given base value)
- sf5.setValue("ID", "sequence_variant:rs758803215");
- dna.addSequenceFeature(sf5);
- SequenceFeature sf6 = new SequenceFeature("sequence_variant", "", 14,
- 14, 0f, null);
- sf6.setValue("alleles", "g, a"); // should force to upper-case
- sf6.setValue("ID", "sequence_variant:rs758803216");
- dna.addSequenceFeature(sf6);
-
- SequenceFeature sf7 = new SequenceFeature("sequence_variant", "", 15,
- 15, 0f, null);
- sf7.setValue("alleles", "A, T");
- sf7.setValue("ID", "sequence_variant:rs758803217");
- dna.addSequenceFeature(sf7);
-
- /*
- * build map - expect variants on positions 1, 2, 3, 5
- */
- variantsMap = AlignmentUtils.buildDnaVariantsMap(dna, map);
- assertEquals(4, variantsMap.size());
-
- /*
- * protein residue 1: variant on codon (ATG) base 1, not on 2 or 3
- */
- List<DnaVariant>[] pep1Variants = variantsMap.get(1);
- assertEquals(3, pep1Variants.length);
- assertEquals(1, pep1Variants[0].size());
- assertEquals("A", pep1Variants[0].get(0).base); // codon[1] base
- assertSame(sf1, pep1Variants[0].get(0).variant); // codon[1] variant
- assertEquals(1, pep1Variants[1].size());
- assertEquals("T", pep1Variants[1].get(0).base); // codon[2] base
- assertNull(pep1Variants[1].get(0).variant); // no variant here
- assertEquals(1, pep1Variants[2].size());
- assertEquals("G", pep1Variants[2].get(0).base); // codon[3] base
- assertNull(pep1Variants[2].get(0).variant); // no variant here
-
- /*
- * protein residue 2: variants on codon (AAA) bases 2 and 3
- */
- List<DnaVariant>[] pep2Variants = variantsMap.get(2);
- assertEquals(3, pep2Variants.length);
- assertEquals(1, pep2Variants[0].size());
- // codon[1] base recorded while processing variant on codon[2]
- assertEquals("A", pep2Variants[0].get(0).base);
- assertNull(pep2Variants[0].get(0).variant); // no variant here
- // codon[2] base and variant:
- assertEquals(1, pep2Variants[1].size());
- assertEquals("A", pep2Variants[1].get(0).base);
- assertSame(sf2, pep2Variants[1].get(0).variant);
- // codon[3] base was recorded when processing codon[2] variant
- // and then the variant for codon[3] added to it
- assertEquals(1, pep2Variants[2].size());
- assertEquals("A", pep2Variants[2].get(0).base);
- assertSame(sf3, pep2Variants[2].get(0).variant);
-
- /*
- * protein residue 3: variants on codon (TTT) base 2 only
- */
- List<DnaVariant>[] pep3Variants = variantsMap.get(3);
- assertEquals(3, pep3Variants.length);
- assertEquals(1, pep3Variants[0].size());
- assertEquals("T", pep3Variants[0].get(0).base); // codon[1] base
- assertNull(pep3Variants[0].get(0).variant); // no variant here
- assertEquals(1, pep3Variants[1].size());
- assertEquals("T", pep3Variants[1].get(0).base); // codon[2] base
- assertSame(sf4, pep3Variants[1].get(0).variant); // codon[2] variant
- assertEquals(1, pep3Variants[2].size());
- assertEquals("T", pep3Variants[2].get(0).base); // codon[3] base
- assertNull(pep3Variants[2].get(0).variant); // no variant here
-
- /*
- * three variants on protein position 5
- */
- List<DnaVariant>[] pep5Variants = variantsMap.get(5);
- assertEquals(3, pep5Variants.length);
- assertEquals(1, pep5Variants[0].size());
- assertEquals("C", pep5Variants[0].get(0).base); // codon[1] base
- assertSame(sf5, pep5Variants[0].get(0).variant); // codon[1] variant
- assertEquals(1, pep5Variants[1].size());
- assertEquals("C", pep5Variants[1].get(0).base); // codon[2] base
- assertSame(sf6, pep5Variants[1].get(0).variant); // codon[2] variant
- assertEquals(1, pep5Variants[2].size());
- assertEquals("C", pep5Variants[2].get(0).base); // codon[3] base
- assertSame(sf7, pep5Variants[2].get(0).variant); // codon[3] variant
- }
-
- /**
- * Tests for the method that computes all peptide variants given codon
- * variants
- */
- @Test(groups = "Functional")
- public void testComputePeptideVariants()
- {
- /*
- * scenario: AAATTTCCC codes for KFP
- * variants:
- * GAA -> E source: Ensembl
- * CAA -> Q source: dbSNP
- * TAA -> STOP source: dnSNP
- * AAG synonymous source: COSMIC
- * AAT -> N source: Ensembl
- * ...TTC synonymous source: dbSNP
- * ......CAC,CGC -> H,R source: COSMIC
- * (one variant with two alleles)
- */
- SequenceI peptide = new Sequence("pep/10-12", "KFP");
-
- /*
- * two distinct variants for codon 1 position 1
- * second one has clinical significance
- */
- String ensembl = "Ensembl";
- String dbSnp = "dbSNP";
- String cosmic = "COSMIC";
-
- /*
- * NB setting "id" (as returned by Ensembl for features in JSON format);
- * previously "ID" (as returned for GFF3 format)
- */
- SequenceFeature sf1 = new SequenceFeature("sequence_variant", "", 1, 1,
- 0f, ensembl);
- sf1.setValue("alleles", "A,G"); // AAA -> GAA -> K/E
- sf1.setValue("id", "var1.125A>G");
-
- SequenceFeature sf2 = new SequenceFeature("sequence_variant", "", 1, 1,
- 0f, dbSnp);
- sf2.setValue("alleles", "A,C"); // AAA -> CAA -> K/Q
- sf2.setValue("id", "var2");
- sf2.setValue("clinical_significance", "Dodgy");
-
- SequenceFeature sf3 = new SequenceFeature("sequence_variant", "", 1, 1,
- 0f, dbSnp);
- sf3.setValue("alleles", "A,T"); // AAA -> TAA -> stop codon
- sf3.setValue("id", "var3");
- sf3.setValue("clinical_significance", "Bad");
-
- SequenceFeature sf4 = new SequenceFeature("sequence_variant", "", 3, 3,
- 0f, cosmic);
- sf4.setValue("alleles", "A,G"); // AAA -> AAG synonymous
- sf4.setValue("id", "var4");
- sf4.setValue("clinical_significance", "None");
-
- SequenceFeature sf5 = new SequenceFeature("sequence_variant", "", 3, 3,
- 0f, ensembl);
- sf5.setValue("alleles", "A,T"); // AAA -> AAT -> K/N
- sf5.setValue("id", "sequence_variant:var5"); // prefix gets stripped off
- sf5.setValue("clinical_significance", "Benign");
-
- SequenceFeature sf6 = new SequenceFeature("sequence_variant", "", 6, 6,
- 0f, dbSnp);
- sf6.setValue("alleles", "T,C"); // TTT -> TTC synonymous
- sf6.setValue("id", "var6");
-
- SequenceFeature sf7 = new SequenceFeature("sequence_variant", "", 8, 8,
- 0f, cosmic);
- sf7.setValue("alleles", "C,A,G"); // CCC -> CAC,CGC -> P/H/R
- sf7.setValue("id", "var7");
- sf7.setValue("clinical_significance", "Good");
-
- List<DnaVariant> codon1Variants = new ArrayList<>();
- List<DnaVariant> codon2Variants = new ArrayList<>();
- List<DnaVariant> codon3Variants = new ArrayList<>();
-
- List<DnaVariant> codonVariants[] = new ArrayList[3];
- codonVariants[0] = codon1Variants;
- codonVariants[1] = codon2Variants;
- codonVariants[2] = codon3Variants;
-
- /*
- * compute variants for protein position 1
- */
- codon1Variants.add(new DnaVariant("A", sf1));
- codon1Variants.add(new DnaVariant("A", sf2));
- codon1Variants.add(new DnaVariant("A", sf3));
- codon2Variants.add(new DnaVariant("A"));
- // codon2Variants.add(new DnaVariant("A"));
- codon3Variants.add(new DnaVariant("A", sf4));
- codon3Variants.add(new DnaVariant("A", sf5));
- AlignmentUtils.computePeptideVariants(peptide, 1, codonVariants);
-
- /*
- * compute variants for protein position 2
- */
- codon1Variants.clear();
- codon2Variants.clear();
- codon3Variants.clear();
- codon1Variants.add(new DnaVariant("T"));
- codon2Variants.add(new DnaVariant("T"));
- codon3Variants.add(new DnaVariant("T", sf6));
- AlignmentUtils.computePeptideVariants(peptide, 2, codonVariants);
-
- /*
- * compute variants for protein position 3
- */
- codon1Variants.clear();
- codon2Variants.clear();
- codon3Variants.clear();
- codon1Variants.add(new DnaVariant("C"));
- codon2Variants.add(new DnaVariant("C", sf7));
- codon3Variants.add(new DnaVariant("C"));
- AlignmentUtils.computePeptideVariants(peptide, 3, codonVariants);
-
- /*
- * verify added sequence features for
- * var1 K -> E Ensembl
- * var2 K -> Q dbSNP
- * var3 K -> stop
- * var4 synonymous
- * var5 K -> N Ensembl
- * var6 synonymous
- * var7 P -> H COSMIC
- * var8 P -> R COSMIC
- */
- List<SequenceFeature> sfs = peptide.getSequenceFeatures();
- SequenceFeatures.sortFeatures(sfs, true);
- assertEquals(8, sfs.size());
-
- /*
- * features are sorted by start position ascending, but in no
- * particular order where start positions match; asserts here
- * simply match the data returned (the order is not important)
- */
- // AAA -> AAT -> K/N
- SequenceFeature sf = sfs.get(0);
- assertEquals(1, sf.getBegin());
- assertEquals(1, sf.getEnd());
- assertEquals("nonsynonymous_variant", sf.getType());
- assertEquals("p.Lys1Asn", sf.getDescription());
- assertEquals("var5", sf.getValue("id"));
- assertEquals("Benign", sf.getValue("clinical_significance"));
- assertEquals("id=var5;clinical_significance=Benign",
- sf.getAttributes());
- assertEquals(1, sf.links.size());
- assertEquals(
- "p.Lys1Asn var5|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var5",
- sf.links.get(0));
- assertEquals(ensembl, sf.getFeatureGroup());
-
- // AAA -> CAA -> K/Q
- sf = sfs.get(1);
- assertEquals(1, sf.getBegin());
- assertEquals(1, sf.getEnd());
- assertEquals("nonsynonymous_variant", sf.getType());
- assertEquals("p.Lys1Gln", sf.getDescription());
- assertEquals("var2", sf.getValue("id"));
- assertEquals("Dodgy", sf.getValue("clinical_significance"));
- assertEquals("id=var2;clinical_significance=Dodgy", sf.getAttributes());
- assertEquals(1, sf.links.size());
- assertEquals(
- "p.Lys1Gln var2|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var2",
- sf.links.get(0));
- assertEquals(dbSnp, sf.getFeatureGroup());
-
- // AAA -> GAA -> K/E
- sf = sfs.get(2);
- assertEquals(1, sf.getBegin());
- assertEquals(1, sf.getEnd());
- assertEquals("nonsynonymous_variant", sf.getType());
- assertEquals("p.Lys1Glu", sf.getDescription());
- assertEquals("var1.125A>G", sf.getValue("id"));
- assertNull(sf.getValue("clinical_significance"));
- assertEquals("id=var1.125A>G", sf.getAttributes());
- assertEquals(1, sf.links.size());
- // link to variation is urlencoded
- assertEquals(
- "p.Lys1Glu var1.125A>G|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var1.125A%3EG",
- sf.links.get(0));
- assertEquals(ensembl, sf.getFeatureGroup());
-
- // AAA -> TAA -> stop codon
- sf = sfs.get(3);
- assertEquals(1, sf.getBegin());
- assertEquals(1, sf.getEnd());
- assertEquals("stop_gained", sf.getType());
- assertEquals("Aaa/Taa", sf.getDescription());
- assertEquals("var3", sf.getValue("id"));
- assertEquals("Bad", sf.getValue("clinical_significance"));
- assertEquals("id=var3;clinical_significance=Bad", sf.getAttributes());
- assertEquals(1, sf.links.size());
- assertEquals(
- "Aaa/Taa var3|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var3",
- sf.links.get(0));
- assertEquals(dbSnp, sf.getFeatureGroup());
-
- // AAA -> AAG synonymous
- sf = sfs.get(4);
- assertEquals(1, sf.getBegin());
- assertEquals(1, sf.getEnd());
- assertEquals("synonymous_variant", sf.getType());
- assertEquals("aaA/aaG", sf.getDescription());
- assertEquals("var4", sf.getValue("id"));
- assertEquals("None", sf.getValue("clinical_significance"));
- assertEquals("id=var4;clinical_significance=None", sf.getAttributes());
- assertEquals(1, sf.links.size());
- assertEquals(
- "aaA/aaG var4|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var4",
- sf.links.get(0));
- assertEquals(cosmic, sf.getFeatureGroup());
-
- // TTT -> TTC synonymous
- sf = sfs.get(5);
- assertEquals(2, sf.getBegin());
- assertEquals(2, sf.getEnd());
- assertEquals("synonymous_variant", sf.getType());
- assertEquals("ttT/ttC", sf.getDescription());
- assertEquals("var6", sf.getValue("id"));
- assertNull(sf.getValue("clinical_significance"));
- assertEquals("id=var6", sf.getAttributes());
- assertEquals(1, sf.links.size());
- assertEquals(
- "ttT/ttC var6|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var6",
- sf.links.get(0));
- assertEquals(dbSnp, sf.getFeatureGroup());
-
- // var7 generates two distinct protein variant features (two alleles)
- // CCC -> CGC -> P/R
- sf = sfs.get(6);
- assertEquals(3, sf.getBegin());
- assertEquals(3, sf.getEnd());
- assertEquals("nonsynonymous_variant", sf.getType());
- assertEquals("p.Pro3Arg", sf.getDescription());
- assertEquals("var7", sf.getValue("id"));
- assertEquals("Good", sf.getValue("clinical_significance"));
- assertEquals("id=var7;clinical_significance=Good", sf.getAttributes());
- assertEquals(1, sf.links.size());
- assertEquals(
- "p.Pro3Arg var7|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var7",
- sf.links.get(0));
- assertEquals(cosmic, sf.getFeatureGroup());
-
- // CCC -> CAC -> P/H
- sf = sfs.get(7);
- assertEquals(3, sf.getBegin());
- assertEquals(3, sf.getEnd());
- assertEquals("nonsynonymous_variant", sf.getType());
- assertEquals("p.Pro3His", sf.getDescription());
- assertEquals("var7", sf.getValue("id"));
- assertEquals("Good", sf.getValue("clinical_significance"));
- assertEquals("id=var7;clinical_significance=Good", sf.getAttributes());
- assertEquals(1, sf.links.size());
- assertEquals(
- "p.Pro3His var7|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var7",
- sf.links.get(0));
- assertEquals(cosmic, sf.getFeatureGroup());
- }
-
- /**
* Tests for the method that maps the subset of a dna sequence that has CDS
* (or subtype) feature, with CDS strand = '-' (reverse)
*/
AlignmentI al2 = new Alignment(new SequenceI[] { dna3, dna4 });
((Alignment) al2).createDatasetAlignment();
+ /*
+ * alignment removes gapped columns (two internal, two trailing)
+ */
assertTrue(AlignmentUtils.alignAsSameSequences(al1, al2));
- assertEquals(seq1, al1.getSequenceAt(0).getSequenceAsString());
- assertEquals(seq2, al1.getSequenceAt(1).getSequenceAsString());
+ String aligned1 = "-cc-GG-GTTT-aaa";
+ assertEquals(aligned1,
+ al1.getSequenceAt(0).getSequenceAsString());
+ String aligned2 = "C--C-Cgg-gtttAAA";
+ assertEquals(aligned2,
+ al1.getSequenceAt(1).getSequenceAsString());
/*
* add another sequence to 'aligned' - should still succeed, since
dna5.createDatasetSequence();
al2.addSequence(dna5);
assertTrue(AlignmentUtils.alignAsSameSequences(al1, al2));
- assertEquals(seq1, al1.getSequenceAt(0).getSequenceAsString());
- assertEquals(seq2, al1.getSequenceAt(1).getSequenceAsString());
+ assertEquals(aligned1, al1.getSequenceAt(0).getSequenceAsString());
+ assertEquals(aligned2, al1.getSequenceAt(1).getSequenceAsString());
/*
* add another sequence to 'unaligned' - should fail, since now not
{
SequenceI dna1 = new Sequence("dna1", "cccGGGTTTaaa");
SequenceI dna2 = new Sequence("dna2", "CCCgggtttAAA");
- SequenceI as1 = dna1.deriveSequence();
- SequenceI as2 = dna1.deriveSequence().getSubSequence(3, 7);
- SequenceI as3 = dna2.deriveSequence();
+ SequenceI as1 = dna1.deriveSequence(); // cccGGGTTTaaa/1-12
+ SequenceI as2 = dna1.deriveSequence().getSubSequence(3, 7); // GGGT/4-7
+ SequenceI as3 = dna2.deriveSequence(); // CCCgggtttAAA/1-12
as1.insertCharAt(6, 5, '-');
- String s_as1 = as1.getSequenceAsString();
+ assertEquals("cccGGG-----TTTaaa", as1.getSequenceAsString());
as2.insertCharAt(6, 5, '-');
- String s_as2 = as2.getSequenceAsString();
- as3.insertCharAt(6, 5, '-');
- String s_as3 = as3.getSequenceAsString();
+ assertEquals("GGGT-----", as2.getSequenceAsString());
+ as3.insertCharAt(3, 5, '-');
+ assertEquals("CCC-----gggtttAAA", as3.getSequenceAsString());
AlignmentI aligned = new Alignment(new SequenceI[] { as1, as2, as3 });
// why do we need to cast this still ?
uas3 });
((Alignment) tobealigned).createDatasetAlignment();
+ /*
+ * alignAs lines up dataset sequences and removes empty columns (two)
+ */
assertTrue(AlignmentUtils.alignAsSameSequences(tobealigned, aligned));
- assertEquals(s_as1, uas1.getSequenceAsString());
- assertEquals(s_as2, uas2.getSequenceAsString());
- assertEquals(s_as3, uas3.getSequenceAsString());
+ assertEquals("cccGGG---TTTaaa", uas1.getSequenceAsString());
+ assertEquals("GGGT", uas2.getSequenceAsString());
+ assertEquals("CCC---gggtttAAA", uas3.getSequenceAsString());
}
@Test(groups = { "Functional" })
* transcript 'CDS' is 10-16, 17-21
* which is 'gene' 158-164, 210-214
*/
- MapList toMap = toLoci.getMap();
+ MapList toMap = toLoci.getMapping();
assertEquals(1, toMap.getFromRanges().size());
assertEquals(2, toMap.getFromRanges().get(0).length);
assertEquals(1, toMap.getFromRanges().get(0)[0]);
AlignmentUtils.transferGeneLoci(from, map, to);
assertEquals("GRCh38", toLoci.getAssemblyId());
assertEquals("7", toLoci.getChromosomeId());
- toMap = toLoci.getMap();
+ toMap = toLoci.getMapping();
assertEquals("[ [1, 12] ] 1:1 to [ [158, 164] [210, 214] ]",
toMap.toString());
}
12.5f, "group");
sf1.setValue("STRAND", "+");
sf1.setValue("Note", "Testing");
- Integer count = new Integer(7);
+ Integer count = Integer.valueOf(7);
sf1.setValue("Count", count);
SequenceFeature sf2 = new SequenceFeature(sf1);
assertEquals("+", sf1.getValue("STRAND"));
assertNull(sf1.getValue("strand")); // case-sensitive
assertEquals(".", sf1.getValue("unknown", "."));
- Integer i = new Integer(27);
+ Integer i = Integer.valueOf(27);
assertSame(i, sf1.getValue("Unknown", i));
}
@Test(groups = { "Functional" })
public void testGetDetailsReport()
{
+ SequenceI seq = new Sequence("TestSeq", "PLRFQMD");
+ String seqName = seq.getName();
+
// single locus, no group, no score
SequenceFeature sf = new SequenceFeature("variant", "G,C", 22, 22, null);
- String expected = "<br><table><tr><td>Type</td><td>variant</td><td></td></tr>"
- + "<tr><td>Start/end</td><td>22</td><td></td></tr>"
+ String expected = "<br><table><tr><td>Location</td><td>TestSeq</td><td>22</td></tr>"
+ + "<tr><td>Type</td><td>variant</td><td></td></tr>"
+ "<tr><td>Description</td><td>G,C</td><td></td></tr></table>";
- assertEquals(expected, sf.getDetailsReport());
+ assertEquals(expected, sf.getDetailsReport(seqName));
// contact feature
sf = new SequenceFeature("Disulphide Bond", "a description", 28, 31,
null);
- expected = "<br><table><tr><td>Type</td><td>Disulphide Bond</td><td></td></tr>"
- + "<tr><td>Start/end</td><td>28:31</td><td></td></tr>"
+ expected = "<br><table><tr><td>Location</td><td>TestSeq</td><td>28:31</td></tr>"
+ + "<tr><td>Type</td><td>Disulphide Bond</td><td></td></tr>"
+ "<tr><td>Description</td><td>a description</td><td></td></tr></table>";
- assertEquals(expected, sf.getDetailsReport());
+ assertEquals(expected, sf.getDetailsReport(seqName));
sf = new SequenceFeature("variant", "G,C", 22, 33,
12.5f, "group");
sf.setValue("Parent", "ENSG001");
sf.setValue("Child", "ENSP002");
- expected = "<br><table><tr><td>Type</td><td>variant</td><td></td></tr>"
- + "<tr><td>Start/end</td><td>22-33</td><td></td></tr>"
+ expected = "<br><table><tr><td>Location</td><td>TestSeq</td><td>22-33</td></tr>"
+ + "<tr><td>Type</td><td>variant</td><td></td></tr>"
+ "<tr><td>Description</td><td>G,C</td><td></td></tr>"
+ "<tr><td>Score</td><td>12.5</td><td></td></tr>"
+ "<tr><td>Group</td><td>group</td><td></td></tr>"
+ "<tr><td>Child</td><td></td><td>ENSP002</td></tr>"
+ "<tr><td>Parent</td><td></td><td>ENSG001</td></tr></table>";
- assertEquals(expected, sf.getDetailsReport());
+ assertEquals(expected, sf.getDetailsReport(seqName));
/*
* feature with embedded html link in description
*/
String desc = "<html>Fer2 Status: True Positive <a href=\"http://pfam.xfam.org/family/PF00111\">Pfam 8_8</a></html>";
sf = new SequenceFeature("Pfam", desc, 8, 83, "Uniprot");
- expected = "<br><table><tr><td>Type</td><td>Pfam</td><td></td></tr>"
- + "<tr><td>Start/end</td><td>8-83</td><td></td></tr>"
+ expected = "<br><table><tr><td>Location</td><td>TestSeq</td><td>8-83</td></tr>"
+ + "<tr><td>Type</td><td>Pfam</td><td></td></tr>"
+ "<tr><td>Description</td><td>Fer2 Status: True Positive <a href=\"http://pfam.xfam.org/family/PF00111\">Pfam 8_8</a></td><td></td></tr>"
+ "<tr><td>Group</td><td>Uniprot</td><td></td></tr></table>";
- assertEquals(expected, sf.getDetailsReport());
+ assertEquals(expected, sf.getDetailsReport(seqName));
}
}
package jalview.ext.ensembl;
import static org.testng.AssertJUnit.assertEquals;
-import static org.testng.AssertJUnit.assertSame;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceI;
-import jalview.datamodel.features.SequenceFeatures;
import jalview.gui.JvOptionPane;
import jalview.io.DataSourceType;
import jalview.io.FastaFile;
import jalview.io.gff.SequenceOntologyLite;
import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.List;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
SequenceFeature sf = new SequenceFeature("sequence_variant", alleles,
1, 2, 0f, null);
sf.setValue("alleles", alleles);
- sf.setAttributes("x=y,z;alleles=" + alleles + ";a=b,c");
EnsemblSeqProxy.reverseComplementAlleles(sf);
String revcomp = "G,C,GTA-,HGMD_MUTATION,gtc";
assertEquals(revcomp, sf.getDescription());
// verify alleles attribute is updated with reverse complement
assertEquals(revcomp, sf.getValue("alleles"));
- // verify attributes string is updated with reverse complement
- assertEquals("x=y,z;alleles=" + revcomp + ";a=b,c", sf.getAttributes());
}
}
final String _inFile = "examples/3W5V.pdb";
inFile = _inFile;
FileLoader fl = new FileLoader();
- fl.loadFile(af.getCurrentView(), _inFile, DataSourceType.FILE,
+ fl.LoadFile(af.getCurrentView(), _inFile, DataSourceType.FILE,
FileFormat.PDB);
try
{
public void testBuildColourCommands()
{
- Map<Object, AtomSpecModel> map = new LinkedHashMap<>();
- ChimeraCommands.addColourRange(map, Color.blue, 0, 2, 5, "A");
- ChimeraCommands.addColourRange(map, Color.blue, 0, 7, 7, "B");
- ChimeraCommands.addColourRange(map, Color.blue, 0, 9, 23, "A");
- ChimeraCommands.addColourRange(map, Color.blue, 1, 1, 1, "A");
- ChimeraCommands.addColourRange(map, Color.blue, 1, 4, 7, "B");
- ChimeraCommands.addColourRange(map, Color.yellow, 1, 8, 8, "A");
- ChimeraCommands.addColourRange(map, Color.yellow, 1, 3, 5, "A");
- ChimeraCommands.addColourRange(map, Color.red, 0, 3, 5, "A");
- ChimeraCommands.addColourRange(map, Color.red, 0, 6, 9, "A");
+ Map<Object, AtomSpecModel> map = new LinkedHashMap<Object, AtomSpecModel>();
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, 0, 2, 5, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, 0, 7, 7, "B");
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, 0, 9, 23, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, 1, 1, 1, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, 1, 4, 7, "B");
+ ChimeraCommands.addAtomSpecRange(map, Color.yellow, 1, 8, 8, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.yellow, 1, 3, 5, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.red, 0, 3, 5, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.red, 0, 6, 9, "A");
// Colours should appear in the Chimera command in the order in which
// they were added; within colour, by model, by chain, ranges in start order
/*
* make a map of { featureType, {featureValue, {residue range specification } } }
*/
- Map<String, Map<Object, AtomSpecModel>> featuresMap = new LinkedHashMap<>();
- Map<Object, AtomSpecModel> featureValues = new HashMap<>();
+ Map<String, Map<Object, AtomSpecModel>> featuresMap = new LinkedHashMap<String, Map<Object, AtomSpecModel>>();
+ Map<Object, AtomSpecModel> featureValues = new HashMap<Object, AtomSpecModel>();
/*
* start with just one feature/value...
*/
featuresMap.put("chain", featureValues);
- ChimeraCommands.addColourRange(featureValues, "X", 0, 8, 20, "A");
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 8, 20, "A");
List<String> commands = ChimeraCommands
.buildSetAttributeCommands(featuresMap);
assertEquals(commands.get(0), "setattr r jv_chain 'X' #0:8-20.A");
// add same feature value, overlapping range
- ChimeraCommands.addColourRange(featureValues, "X", 0, 3, 9, "A");
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 3, 9, "A");
// same feature value, contiguous range
- ChimeraCommands.addColourRange(featureValues, "X", 0, 21, 25, "A");
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 21, 25, "A");
commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
assertEquals(1, commands.size());
assertEquals(commands.get(0), "setattr r jv_chain 'X' #0:3-25.A");
// same feature value and model, different chain
- ChimeraCommands.addColourRange(featureValues, "X", 0, 21, 25, "B");
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 21, 25, "B");
// same feature value and chain, different model
- ChimeraCommands.addColourRange(featureValues, "X", 1, 26, 30, "A");
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", 1, 26, 30, "A");
commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
assertEquals(1, commands.size());
assertEquals(commands.get(0),
"setattr r jv_chain 'X' #0:3-25.A,21-25.B|#1:26-30.A");
// same feature, different value
- ChimeraCommands.addColourRange(featureValues, "Y", 0, 40, 50, "A");
+ ChimeraCommands.addAtomSpecRange(featureValues, "Y", 0, 40, 50, "A");
commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
assertEquals(2, commands.size());
// commands are ordered by feature type but not by value
featuresMap.clear();
featureValues.clear();
featuresMap.put("side-chain binding!", featureValues);
- ChimeraCommands.addColourRange(featureValues,
+ ChimeraCommands.addAtomSpecRange(featureValues,
"<html>metal <a href=\"http:a.b.c/x\"> 'ion!", 0, 7, 15,
"A");
// feature names are sanitised to change non-alphanumeric to underscore
import jalview.gui.Desktop;
import jalview.gui.JvOptionPane;
import jalview.schemes.FeatureColour;
+import jalview.structure.StructureSelectionManager;
import jalview.util.matcher.Condition;
import jalview.viewmodel.seqfeatures.FeatureRendererModel;
import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
public class FeaturesFileTest
{
- private static final String LINE_SEPARATOR = System.getProperty("line.separator");
private static String simpleGffFile = "examples/testdata/simpleGff3.gff";
@AfterClass(alwaysRun = true)
/*
* remove any sequence mappings created so they don't pollute other tests
*/
- Desktop.getStructureSelectionManager().resetAll();
+ StructureSelectionManager ssm = StructureSelectionManager
+ .getStructureSelectionManager(Desktop.getInstance());
+ ssm.resetAll();
}
@BeforeClass(alwaysRun = true)
AlignFrame af = new AlignFrame(al, 500, 500);
Map<String, FeatureColourI> colours = af.getFeatureRenderer()
.getFeatureColours();
- // GFF3 uses '=' separator for name/value pairs in colum 9
+ // GFF3 uses '=' separator for name/value pairs in column 9
+ // comma (%2C) equals (%3D) or semi-colon (%3B) should be url-escaped in values
String gffData = "##gff-version 3\n"
+ "FER_CAPAA\tuniprot\tMETAL\t39\t39\t0.0\t.\t.\t"
- + "Note=Iron-sulfur (2Fe-2S);Note=another note;evidence=ECO:0000255|PROSITE-ProRule:PRU00465\n"
+ + "Note=Iron-sulfur (2Fe-2S);Note=another note,and another;evidence=ECO%3B0000255%2CPROSITE%3DProRule:PRU00465;"
+ + "CSQ=AF=21,POLYPHEN=benign,possibly_damaging,clin_sig=Benign%3Dgood\n"
+ "FER1_SOLLC\tuniprot\tPfam\t55\t130\t3.0\t.\t.\tID=$23";
FeaturesFile featuresFile = new FeaturesFile(gffData,
DataSourceType.PASTE);
assertEquals(1, sfs.size());
SequenceFeature sf = sfs.get(0);
// description parsed from Note attribute
- assertEquals("Iron-sulfur (2Fe-2S),another note", sf.description);
+ assertEquals("Iron-sulfur (2Fe-2S),another note,and another",
+ sf.description);
assertEquals(39, sf.begin);
assertEquals(39, sf.end);
assertEquals("uniprot", sf.featureGroup);
assertEquals("METAL", sf.type);
- assertEquals(
- "Note=Iron-sulfur (2Fe-2S);Note=another note;evidence=ECO:0000255|PROSITE-ProRule:PRU00465",
- sf.getValue("ATTRIBUTES"));
+ assertEquals(5, sf.otherDetails.size());
+ assertEquals("ECO;0000255,PROSITE=ProRule:PRU00465", // url decoded
+ sf.getValue("evidence"));
+ assertEquals("Iron-sulfur (2Fe-2S),another note,and another",
+ sf.getValue("Note"));
+ assertEquals("21", sf.getValueAsString("CSQ", "AF"));
+ assertEquals("benign,possibly_damaging",
+ sf.getValueAsString("CSQ", "POLYPHEN"));
+ assertEquals("Benign=good", sf.getValueAsString("CSQ", "clin_sig")); // url decoded
+ // todo change STRAND and !Phase into fields of SequenceFeature instead
+ assertEquals(".", sf.otherDetails.get("STRAND"));
+ assertEquals(0, sf.getStrand());
+ assertEquals(".", sf.getPhase());
// verify feature on FER1_SOLLC1
sfs = al.getSequenceAt(2).getDatasetSequence().getSequenceFeatures();
*/
FeatureRenderer fr = af.alignPanel.getFeatureRenderer();
String exported = featuresFile
- .printJalviewFormat(al.getSequencesArray(), fr, false);
+ .printJalviewFormat(al.getSequencesArray(), fr, false, false);
String expected = "No Features Visible";
assertEquals(expected, exported);
*/
fr.setGroupVisibility("uniprot", true);
exported = featuresFile.printJalviewFormat(al.getSequencesArray(), fr,
- true);
+ true, false);
expected = "\nSTARTGROUP\tuniprot\n"
+ "Cath\tFER_CAPAA\t-1\t0\t0\tDomain\t0.0\n"
+ "ENDGROUP\tuniprot\n\n"
+ "desc1\tFER_CAPAN\t-1\t0\t0\tPfam\t1.3\n\n"
+ "desc3\tFER1_SOLLC\t-1\t0\t0\tPfam\n"; // NaN is not output
- assertEquals(
- expected.replace("\n", LINE_SEPARATOR),
- exported);
+ assertEquals(expected, exported);
/*
* set METAL (in uniprot group) and GAMMA-TURN visible, but not Pfam
fr.setVisible("METAL");
fr.setVisible("GAMMA-TURN");
exported = featuresFile.printJalviewFormat(al.getSequencesArray(), fr,
- false);
+ false, false);
expected = "METAL\tcc9900\n"
+ "GAMMA-TURN\tscore|ff0000|00ffff|noValueMin|20.0|95.0|below|66.0\n"
+ "\nSTARTGROUP\tuniprot\n"
- + "Turn\tFER_CAPAA\t-1\t36\t38\tGAMMA-TURN\t0.0\n"
+ "Iron\tFER_CAPAA\t-1\t39\t39\tMETAL\t0.0\n"
+ + "Turn\tFER_CAPAA\t-1\t36\t38\tGAMMA-TURN\t0.0\n"
+ "ENDGROUP\tuniprot\n";
- assertEquals(fixLineEnd(expected), exported);
+ assertEquals(expected, exported);
/*
* now set Pfam visible
*/
fr.setVisible("Pfam");
exported = featuresFile.printJalviewFormat(al.getSequencesArray(), fr,
- false);
+ false, false);
/*
* features are output within group, ordered by sequence and type
*/
+ "Pfam\tff0000\n"
+ "GAMMA-TURN\tscore|ff0000|00ffff|noValueMin|20.0|95.0|below|66.0\n"
+ "\nSTARTGROUP\tuniprot\n"
- + "Turn\tFER_CAPAA\t-1\t36\t38\tGAMMA-TURN\t0.0\n"
+ "Iron\tFER_CAPAA\t-1\t39\t39\tMETAL\t0.0\n"
+ "<html>Pfam domain<a href=\"http://pfam.xfam.org/family/PF00111\">Pfam_3_4</a></html>\tFER_CAPAA\t-1\t20\t20\tPfam\t0.0\n"
+ + "Turn\tFER_CAPAA\t-1\t36\t38\tGAMMA-TURN\t0.0\n"
+ "ENDGROUP\tuniprot\n"
// null / empty group features are output after named groups
+ "\ndesc2\tFER_CAPAN\t-1\t4\t9\tPfam\n"
+ "\ndesc4\tFER1_SOLLC\t-1\t5\t8\tPfam\t-2.6\n";
- assertEquals(fixLineEnd(expected), exported);
+ assertEquals(expected, exported);
/*
* hide uniprot group
+ "\ndesc2\tFER_CAPAN\t-1\t4\t9\tPfam\n"
+ "\ndesc4\tFER1_SOLLC\t-1\t5\t8\tPfam\t-2.6\n";
exported = featuresFile.printJalviewFormat(al.getSequencesArray(), fr,
- false);
- assertEquals(fixLineEnd(expected), exported);
+ false, false);
+ assertEquals(expected, exported);
/*
* include non-positional (overrides group not shown)
*/
exported = featuresFile.printJalviewFormat(al.getSequencesArray(), fr,
- true);
+ true, false);
expected = "METAL\tcc9900\n" + "Pfam\tff0000\n"
+ "GAMMA-TURN\tscore|ff0000|00ffff|noValueMin|20.0|95.0|below|66.0\n"
+ "\nSTARTGROUP\tuniprot\n"
+ "desc2\tFER_CAPAN\t-1\t4\t9\tPfam\n"
+ "\ndesc3\tFER1_SOLLC\t-1\t0\t0\tPfam\n"
+ "desc4\tFER1_SOLLC\t-1\t5\t8\tPfam\t-2.6\n";
- assertEquals(fixLineEnd(expected), exported);
+ assertEquals(expected, exported);
}
@Test(groups = { "Functional" })
FeatureRendererModel fr = (FeatureRendererModel) af.alignPanel
.getFeatureRenderer();
String exported = featuresFile.printGffFormat(al.getSequencesArray(),
- fr, false);
+ fr, false, false);
String gffHeader = "##gff-version 2\n";
- assertEquals(
- fixLineEnd(gffHeader),
- exported);
+ assertEquals(gffHeader, exported);
exported = featuresFile.printGffFormat(al.getSequencesArray(), fr,
- true);
- assertEquals(
- fixLineEnd(gffHeader),
- exported);
+ true, false);
+ assertEquals(gffHeader, exported);
/*
* add some features
"s3dm"));
SequenceFeature sf = new SequenceFeature("Pfam", "", 20, 20, 0f,
"Uniprot");
- sf.setAttributes("x=y;black=white");
sf.setStrand("+");
sf.setPhase("2");
+ sf.setValue("x", "y");
+ sf.setValue("black", "white");
+ Map<String, String> csq = new HashMap<>();
+ csq.put("SIFT", "benign,mostly benign,cloudy, with meatballs");
+ csq.put("consequence", "missense_variant");
+ sf.setValue("CSQ", csq);
al.getSequenceAt(1).addSequenceFeature(sf);
/*
* with no features displayed, exclude non-positional features
*/
exported = featuresFile.printGffFormat(al.getSequencesArray(), fr,
- false);
- assertEquals(
- fixLineEnd(gffHeader),
- exported);
+ false, false);
+ assertEquals(gffHeader, exported);
/*
* include non-positional features
fr.setGroupVisibility("Uniprot", true);
fr.setGroupVisibility("s3dm", false);
exported = featuresFile.printGffFormat(al.getSequencesArray(), fr,
- true);
+ true, false);
String expected = gffHeader
+ "FER_CAPAA\tUniprot\tDomain\t0\t0\t0.0\t.\t.\n";
- assertEquals(
- fixLineEnd(expected),
- exported);
+ assertEquals(expected, exported);
/*
* set METAL (in uniprot group) and GAMMA-TURN visible, but not Pfam
fr.setVisible("METAL");
fr.setVisible("GAMMA-TURN");
exported = featuresFile.printGffFormat(al.getSequencesArray(), fr,
- false);
+ false, false);
// METAL feature has null group: description used for column 2
expected = gffHeader + "FER_CAPAA\tCath\tMETAL\t39\t39\t1.2\t.\t.\n";
- assertEquals(
- expected.replace("\n", LINE_SEPARATOR),
- exported);
+ assertEquals(expected, exported);
/*
* set s3dm group visible
*/
fr.setGroupVisibility("s3dm", true);
exported = featuresFile.printGffFormat(al.getSequencesArray(), fr,
- false);
+ false, false);
// METAL feature has null group: description used for column 2
expected = gffHeader + "FER_CAPAA\tCath\tMETAL\t39\t39\t1.2\t.\t.\n"
+ "FER_CAPAN\ts3dm\tGAMMA-TURN\t36\t38\t2.1\t.\t.\n";
- assertEquals(
- expected.replace("\n", LINE_SEPARATOR),
- exported);
+ assertEquals(expected, exported);
/*
* now set Pfam visible
*/
fr.setVisible("Pfam");
exported = featuresFile.printGffFormat(al.getSequencesArray(), fr,
- false);
+ false, false);
// Pfam feature columns include strand(+), phase(2), attributes
expected = gffHeader
+ "FER_CAPAA\tCath\tMETAL\t39\t39\t1.2\t.\t.\n"
- + "FER_CAPAN\ts3dm\tGAMMA-TURN\t36\t38\t2.1\t.\t.\n"
- + "FER_CAPAN\tUniprot\tPfam\t20\t20\t0.0\t+\t2\tx=y;black=white\n";
- assertEquals(fixLineEnd(expected), exported);
- }
-
- private String fixLineEnd(String s)
- {
- return s.replace("\n", LINE_SEPARATOR);
+ // CSQ output as CSQ=att1=value1,att2=value2
+ // note all commas are encoded here which is wrong - it should be
+ // SIFT=benign,mostly benign,cloudy%2C with meatballs
+ + "FER_CAPAN\tUniprot\tPfam\t20\t20\t0.0\t+\t2\tx=y;black=white;"
+ + "CSQ=SIFT=benign%2Cmostly benign%2Ccloudy%2C with meatballs,consequence=missense_variant\n"
+ + "FER_CAPAN\ts3dm\tGAMMA-TURN\t36\t38\t2.1\t.\t.\n";
+ assertEquals(expected, exported);
}
/**
visible.put("foobar", new FeatureColour(Color.blue));
ff.outputFeatureFilters(sb, visible, featureFilters);
String expected = "\nSTARTFILTERS\nfoobar\tLabel Present\npfam\t(CSQ:PolyPhen Present) AND (Score LE -2.4)\nENDFILTERS\n";
- assertEquals(fixLineEnd(expected), sb.toString());
+ assertEquals(expected, sb.toString());
}
/**
fr.setVisible("METAL");
fr.setColour("METAL", new FeatureColour(Color.PINK));
String exported = featuresFile.printGffFormat(al.getSequencesArray(),
- fr, false);
+ fr, false, false);
String expected = gffHeader
- + "FER_CAPAA\tCath\tMETAL\t39\t39\t1.2\t.\t.\n"
- + "FER_CAPAA\tCath\tMETAL\t41\t41\t0.6\t.\t.\n";
- assertEquals(
- fixLineEnd(expected), exported);
+ + "FER_CAPAA\tCath\tMETAL\t39\t39\t1.2\t.\t.\tclin_sig=Likely Pathogenic;AF=24\n"
+ + "FER_CAPAA\tCath\tMETAL\t41\t41\t0.6\t.\t.\tclin_sig=Benign;AF=46\n";
+ assertEquals(expected, exported);
/*
* now threshold to Score > 1.1 - should exclude sf2
fc.setThreshold(1.1f);
fr.setColour("METAL", fc);
exported = featuresFile.printGffFormat(al.getSequencesArray(), fr,
- false);
- expected = gffHeader + "FER_CAPAA\tCath\tMETAL\t39\t39\t1.2\t.\t.\n";
- assertEquals(fixLineEnd(expected), exported);
+ false, false);
+ expected = gffHeader
+ + "FER_CAPAA\tCath\tMETAL\t39\t39\t1.2\t.\t.\tclin_sig=Likely Pathogenic;AF=24\n";
+ assertEquals(expected, exported);
/*
* remove threshold and check sf2 is exported
*/
fc.setAboveThreshold(false);
exported = featuresFile.printGffFormat(al.getSequencesArray(), fr,
- false);
- expected = gffHeader + "FER_CAPAA\tCath\tMETAL\t39\t39\t1.2\t.\t.\n"
- + "FER_CAPAA\tCath\tMETAL\t41\t41\t0.6\t.\t.\n";
- assertEquals(fixLineEnd(expected), exported);
+ false, false);
+ expected = gffHeader
+ + "FER_CAPAA\tCath\tMETAL\t39\t39\t1.2\t.\t.\tclin_sig=Likely Pathogenic;AF=24\n"
+ + "FER_CAPAA\tCath\tMETAL\t41\t41\t0.6\t.\t.\tclin_sig=Benign;AF=46\n";
+ assertEquals(expected, exported);
/*
* filter on (clin_sig contains Benign) - should include sf2 and exclude sf1
"clin_sig"));
fr.setFeatureFilter("METAL", filter);
exported = featuresFile.printGffFormat(al.getSequencesArray(), fr,
- false);
- expected = gffHeader + "FER_CAPAA\tCath\tMETAL\t41\t41\t0.6\t.\t.\n";
- assertEquals(fixLineEnd(expected), exported);
+ false, false);
+ expected = gffHeader
+ + "FER_CAPAA\tCath\tMETAL\t41\t41\t0.6\t.\t.\tclin_sig=Benign;AF=46\n";
+ assertEquals(expected, exported);
}
/**
fr.setColour("METAL", new FeatureColour(Color.PINK));
String exported = featuresFile.printJalviewFormat(
al.getSequencesArray(),
- fr, false);
+ fr, false, false);
String expected = "METAL\tffafaf\n\nSTARTGROUP\tgrp1\n"
+ "Cath\tFER_CAPAA\t-1\t39\t39\tMETAL\t1.2\n"
+ "ENDGROUP\tgrp1\n\nSTARTGROUP\tgrp2\n"
+ "Cath\tFER_CAPAA\t-1\t41\t41\tMETAL\t0.6\n"
+ "ENDGROUP\tgrp2\n";
- assertEquals(
- fixLineEnd(expected),
- exported);
+ assertEquals(expected, exported);
/*
* now threshold to Score > 1.1 - should exclude sf2
fc.setThreshold(1.1f);
fr.setColour("METAL", fc);
exported = featuresFile.printJalviewFormat(al.getSequencesArray(), fr,
- false);
+ false, false);
expected = "METAL\tscore|ffffff|000000|noValueMin|abso|0.0|2.0|above|1.1\n\n"
+ "STARTGROUP\tgrp1\n"
+ "Cath\tFER_CAPAA\t-1\t39\t39\tMETAL\t1.2\n"
+ "ENDGROUP\tgrp1\n";
- assertEquals(
- expected.replace("\n", LINE_SEPARATOR),
- exported);
+ assertEquals(expected, exported);
/*
* remove threshold and check sf2 is exported
*/
fc.setAboveThreshold(false);
exported = featuresFile.printJalviewFormat(al.getSequencesArray(), fr,
- false);
+ false, false);
expected = "METAL\tscore|ffffff|000000|noValueMin|abso|0.0|2.0|none\n\n"
+ "STARTGROUP\tgrp1\n"
+ "Cath\tFER_CAPAA\t-1\t39\t39\tMETAL\t1.2\n"
+ "ENDGROUP\tgrp1\n\nSTARTGROUP\tgrp2\n"
+ "Cath\tFER_CAPAA\t-1\t41\t41\tMETAL\t0.6\n"
+ "ENDGROUP\tgrp2\n";
- assertEquals(fixLineEnd(expected), exported);
+ assertEquals(expected, exported);
/*
* filter on (clin_sig contains Benign) - should include sf2 and exclude sf1
"clin_sig"));
fr.setFeatureFilter("METAL", filter);
exported = featuresFile.printJalviewFormat(al.getSequencesArray(), fr,
- false);
+ false, false);
expected = "FER_CAPAA\tCath\tMETAL\t41\t41\t0.6\t.\t.\n";
expected = "METAL\tscore|ffffff|000000|noValueMin|abso|0.0|2.0|none\n\n"
+ "STARTFILTERS\nMETAL\tclin_sig Contains benign\nENDFILTERS\n\n"
+ "STARTGROUP\tgrp2\n"
+ "Cath\tFER_CAPAA\t-1\t41\t41\tMETAL\t0.6\n"
+ "ENDGROUP\tgrp2\n";
- assertEquals(fixLineEnd(expected), exported);
+ assertEquals(expected, exported);
}
}
{
String urlFile = "http://www.jalview.org/builds/develop/examples/3W5V.pdb";
FileLoader fileLoader = new FileLoader();
- fileLoader.loadFileWaitTillLoaded(urlFile, DataSourceType.URL,
+ fileLoader.LoadFileWaitTillLoaded(urlFile, DataSourceType.URL,
FileFormat.PDB);
Assert.assertNotNull(fileLoader.file);
// The FileLoader's file is expected to be same as the original URL.
import jalview.viewmodel.seqfeatures.FeatureRendererModel;
import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import org.testng.annotations.BeforeClass;
3, 1.2f, "group");
// residuePos == 2 does not match start or end of feature, nothing done:
- sar.appendFeature(sb, 2, null, sf);
+ sar.appendFeature(sb, 2, null, sf, null);
assertEquals("123456", sb.toString());
- // residuePos == 1 matches start of feature, text appended (but no <br>)
+ // residuePos == 1 matches start of feature, text appended (but no <br/>)
// feature score is not included
- sar.appendFeature(sb, 1, null, sf);
+ sar.appendFeature(sb, 1, null, sf, null);
assertEquals("123456disulfide bond 1:3", sb.toString());
// residuePos == 3 matches end of feature, text appended
- // <br> is prefixed once sb.length() > 6
- sar.appendFeature(sb, 3, null, sf);
- assertEquals("123456disulfide bond 1:3<br>disulfide bond 1:3",
+ // <br/> is prefixed once sb.length() > 6
+ sar.appendFeature(sb, 3, null, sf, null);
+ assertEquals("123456disulfide bond 1:3<br/>disulfide bond 1:3",
sb.toString());
}
@Test(groups = "Functional")
+ public void testAppendFeatures_longText()
+ {
+ SequenceAnnotationReport sar = new SequenceAnnotationReport(null);
+ StringBuilder sb = new StringBuilder();
+ String longString = // java11!"Abcd".repeat(50);
+ "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"
+ + "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"
+ + "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"
+ + "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"
+ + "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcd";
+
+ SequenceFeature sf = new SequenceFeature("sequence", longString, 1, 3,
+ "group");
+
+ sar.appendFeature(sb, 1, null, sf, null);
+ assertTrue(sb.length() < 100);
+
+ List<SequenceFeature> sfl = new ArrayList<>();
+ sb.setLength(0);
+ sfl.add(sf);
+ sfl.add(sf);
+ sfl.add(sf);
+ sfl.add(sf);
+ sfl.add(sf);
+ sfl.add(sf);
+ sfl.add(sf);
+ sfl.add(sf);
+ sfl.add(sf);
+ sfl.add(sf);
+ int n = sar.appendFeaturesLengthLimit(sb, 1, sfl,
+ new FeatureRenderer(null), 200); // text should terminate before 200 characters
+ String s = sb.toString();
+ assertTrue(s.length() < 200);
+ assertEquals(n, 7); // should be 7 features left over
+
+ }
+
+ @Test(groups = "Functional")
public void testAppendFeature_status()
{
SequenceAnnotationReport sar = new SequenceAnnotationReport(null);
Float.NaN, "group");
sf.setStatus("Confirmed");
- sar.appendFeature(sb, 1, null, sf);
+ sar.appendFeature(sb, 1, null, sf, null);
assertEquals("METAL 1 3; Fe2-S; (Confirmed)", sb.toString());
}
FeatureRendererModel fr = new FeatureRenderer(null);
Map<String, float[][]> minmax = fr.getMinMax();
- sar.appendFeature(sb, 1, fr, sf);
+ sar.appendFeature(sb, 1, fr, sf, null);
/*
* map has no entry for this feature type - score is not shown:
*/
* map has entry for this feature type - score is shown:
*/
minmax.put("METAL", new float[][] { { 0f, 1f }, null });
- sar.appendFeature(sb, 1, fr, sf);
- // <br> is appended to a buffer > 6 in length
- assertEquals("METAL 1 3; Fe2-S<br>METAL 1 3; Fe2-S Score=1.3",
+ sar.appendFeature(sb, 1, fr, sf, null);
+ // <br/> is appended to a buffer > 6 in length
+ assertEquals("METAL 1 3; Fe2-S<br/>METAL 1 3; Fe2-S Score=1.3",
sb.toString());
/*
*/
minmax.put("METAL", new float[][] { { 2f, 2f }, null });
sb.setLength(0);
- sar.appendFeature(sb, 1, fr, sf);
+ sar.appendFeature(sb, 1, fr, sf, null);
assertEquals("METAL 1 3; Fe2-S", sb.toString());
}
SequenceFeature sf = new SequenceFeature("METAL", "Fe2-S", 1, 3,
Float.NaN, "group");
- sar.appendFeature(sb, 1, null, sf);
+ sar.appendFeature(sb, 1, null, sf, null);
assertEquals("METAL 1 3; Fe2-S", sb.toString());
}
* first with no colour by attribute
*/
FeatureRendererModel fr = new FeatureRenderer(null);
- sar.appendFeature(sb, 1, fr, sf);
+ sar.appendFeature(sb, 1, fr, sf, null);
assertEquals("METAL 1 3; Fe2-S", sb.toString());
/*
fc.setAttributeName("Pfam");
fr.setColour("METAL", fc);
sb.setLength(0);
- sar.appendFeature(sb, 1, fr, sf);
+ sar.appendFeature(sb, 1, fr, sf, null);
assertEquals("METAL 1 3; Fe2-S", sb.toString()); // no change
/*
*/
fc.setAttributeName("clinical_significance");
sb.setLength(0);
- sar.appendFeature(sb, 1, fr, sf);
+ sar.appendFeature(sb, 1, fr, sf, null);
assertEquals("METAL 1 3; Fe2-S; clinical_significance=Benign",
sb.toString());
}
fc.setAttributeName("clinical_significance");
fr.setColour("METAL", fc);
minmax.put("METAL", new float[][] { { 0f, 1f }, null });
- sar.appendFeature(sb, 1, fr, sf);
+ sar.appendFeature(sb, 1, fr, sf, null);
assertEquals(
"METAL 1 3; Fe2-S Score=1.3; (Confirmed); clinical_significance=Benign",
Float.NaN, "group");
// description is not included if it duplicates type:
- sar.appendFeature(sb, 1, null, sf);
+ sar.appendFeature(sb, 1, null, sf, null);
assertEquals("METAL 1 3", sb.toString());
sb.setLength(0);
sf.setDescription("Metal");
// test is case-sensitive:
- sar.appendFeature(sb, 1, null, sf);
+ sar.appendFeature(sb, 1, null, sf, null);
assertEquals("METAL 1 3; Metal", sb.toString());
}
"<html><body>hello<em>world</em></body></html>", 1, 3,
Float.NaN, "group");
- sar.appendFeature(sb, 1, null, sf);
+ sar.appendFeature(sb, 1, null, sf, null);
// !! strips off </body> but not <body> ??
assertEquals("METAL 1 3; <body>hello<em>world</em>", sb.toString());
sb.setLength(0);
sf.setDescription("<br>&kHD>6");
- sar.appendFeature(sb, 1, null, sf);
+ sar.appendFeature(sb, 1, null, sf, null);
// if no <html> tag, html-encodes > and < (only):
assertEquals("METAL 1 3; <br>&kHD>6", sb.toString());
}
SequenceI seq = new Sequence("s1", "MAKLKRFQSSTLL");
seq.setDescription("SeqDesc");
- sar.createSequenceAnnotationReport(sb, seq, true, true, null);
-
/*
* positional features are ignored
*/
seq.addSequenceFeature(new SequenceFeature("Domain", "Ferredoxin", 5,
10, 1f, null));
- assertEquals("<i><br>SeqDesc</i>", sb.toString());
+ sar.createSequenceAnnotationReport(sb, seq, true, true, null);
+ assertEquals("<i>SeqDesc</i>", sb.toString());
/*
* non-positional feature
null));
sb.setLength(0);
sar.createSequenceAnnotationReport(sb, seq, true, true, null);
- String expected = "<i><br>SeqDesc<br>Type1 ; Nonpos Score=1.0</i>";
+ String expected = "<i>SeqDesc<br/>Type1 ; Nonpos Score=1.0</i>";
assertEquals(expected, sb.toString());
/*
*/
sb.setLength(0);
sar.createSequenceAnnotationReport(sb, seq, true, false, null);
- assertEquals("<i><br>SeqDesc</i>", sb.toString());
+ assertEquals("<i>SeqDesc</i>", sb.toString());
/*
* add non-pos feature with score inside min-max range for feature type
sb.setLength(0);
sar.createSequenceAnnotationReport(sb, seq, true, true, fr);
- expected = "<i><br>SeqDesc<br>Metal ; Desc<br>Type1 ; Nonpos</i>";
+ expected = "<i>SeqDesc<br/>Metal ; Desc<br/>Type1 ; Nonpos</i>";
assertEquals(expected, sb.toString());
/*
assertEquals(expected, sb.toString()); // unchanged!
/*
- * 'clinical_significance' attribute only included when
- * used for feature colouring
+ * 'clinical_significance' attribute is only included in description
+ * when used for feature colouring
*/
SequenceFeature sf2 = new SequenceFeature("Variant", "Havana", 0, 0,
5f, null);
seq.addSequenceFeature(sf2);
sb.setLength(0);
sar.createSequenceAnnotationReport(sb, seq, true, true, fr);
- expected = "<i><br>SeqDesc<br>Metal ; Desc<br>Type1 ; Nonpos<br>Variant ; Havana</i>";
+ expected = "<i>SeqDesc<br/>Metal ; Desc<br/>Type1 ; Nonpos<br/>Variant ; Havana</i>";
assertEquals(expected, sb.toString());
/*
fc.setAttributeName("clinical_significance");
fr.setColour("Variant", fc);
sar.createSequenceAnnotationReport(sb, seq, true, true, fr);
- expected = "<i><br>SeqDesc<br>UNIPROT P30419<br>PDB 3iu1<br>Metal ; Desc<br>"
- + "Type1 ; Nonpos<br>Variant ; Havana; clinical_significance=benign</i>";
+ expected = "<i>SeqDesc<br/>UNIPROT P30419<br/>PDB 3iu1<br/>Metal ; Desc<br/>"
+ + "Type1 ; Nonpos<br/>Variant ; Havana; clinical_significance=benign</i>";
assertEquals(expected, sb.toString());
// with showNonPositionalFeatures = false
sb.setLength(0);
sar.createSequenceAnnotationReport(sb, seq, true, false, fr);
- expected = "<i><br>SeqDesc<br>UNIPROT P30419<br>PDB 3iu1</i>";
+ expected = "<i>SeqDesc<br/>UNIPROT P30419<br/>PDB 3iu1</i>";
+ assertEquals(expected, sb.toString());
+
+ /*
+ * long feature description is truncated with ellipsis
+ */
+ sb.setLength(0);
+ sf2.setDescription(
+ "This is a very long description which should be truncated");
+ sar.createSequenceAnnotationReport(sb, seq, false, true, fr);
+ expected = "<i>SeqDesc<br/>Metal ; Desc<br/>Type1 ; Nonpos<br/>Variant ; This is a very long description which sh...; clinical_significance=benign</i>";
assertEquals(expected, sb.toString());
// see other tests for treatment of status and html
sar.createSequenceAnnotationReport(sb, seq, true, true, null, true);
String report = sb.toString();
assertTrue(report
- .startsWith("<i><br>UNIPROT P30410, P30411, P30412, P30413,...<br>PDB0 3iu1"));
+ .startsWith(
+ "<i><br/>UNIPROT P30410, P30411, P30412, P30413,...<br/>PDB0 3iu1"));
assertTrue(report
- .endsWith("<br>PDB7 3iu1<br>PDB8,...<br>(Output Sequence Details to list all database references)</i>"));
+ .endsWith(
+ "<br/>PDB7 3iu1<br/>PDB8,...<br/>(Output Sequence Details to list all database references)</i>"));
}
}
public void mapFer1From3W5V() throws Exception
{
AlignFrame seqf = new FileLoader(false)
- .loadFileWaitTillLoaded(
+ .LoadFileWaitTillLoaded(
">FER1_MAIZE/1-150 Ferredoxin-1, chloroplast precursor\nMATVLGSPRAPAFFFSSSSLRAAPAPTAVALPAAKVGIMGRSASSRRRLRAQATYNVKLITPEGEVELQVPD\nDVYILDQAEEDGIDLPYSCRAGSCSSCAGKVVSGSVDQSDQSYLDDGQIADGWVLTCHAYPTSDVVIETHKE\nEELTGA",
DataSourceType.PASTE, FileFormat.Fasta);
SequenceI newseq = seqf.getViewport().getAlignment().getSequenceAt(0);