/* * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) * Copyright (C) $$Year-Rel$$ The Jalview Authors * * This file is part of Jalview. * * Jalview is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * Jalview is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ package jalview.io; import java.awt.Color; import java.io.BufferedReader; import java.util.ArrayList; import java.util.BitSet; 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; import jalview.analysis.Conservation; import jalview.api.AlignViewportI; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.Annotation; import jalview.datamodel.ColumnSelection; import jalview.datamodel.GraphLine; import jalview.datamodel.HiddenColumns; import jalview.datamodel.HiddenSequences; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.schemes.ColourSchemeI; import jalview.schemes.ColourSchemeProperty; import jalview.util.ColorUtils; public class AnnotationFile { private static final String GRAPHLINE = "GRAPHLINE"; private static final String COMBINE = "COMBINE"; protected String newline = System.getProperty("line.separator"); private StringBuffer text; private SequenceI refSeq = null; private String refSeqId = null; private long nlinesread = 0; private String lastread = ""; /** * Constructor */ public AnnotationFile() { init(); } private void init() { text = new StringBuffer("JALVIEW_ANNOTATION" + newline + "# Created: " + new java.util.Date() + newline + newline); refSeq = null; refSeqId = null; } /** * convenience method for pre-2.9 annotation files which have no view, hidden * columns or hidden row keywords. * * @param annotations * @param list * @param properties * @return annotation file as a string. */ public String printAnnotations(AlignmentAnnotation[] annotations, List list, Hashtable properties) { return printAnnotations(annotations, list, properties, null, null, null); } /** * hold all the information about a particular view definition read from or * written out in an annotations file. */ public class ViewDef { // TODO this class is not used - remove? public final String viewname; public final HiddenSequences hidseqs; public final HiddenColumns hiddencols; public final Hashtable hiddenRepSeqs; public ViewDef(String vname, HiddenSequences hseqs, HiddenColumns hcols, Hashtable hRepSeqs) { this.viewname = vname; this.hidseqs = hseqs; this.hiddencols = hcols; this.hiddenRepSeqs = hRepSeqs; } } /** * Prepare an annotation file given a set of annotations, groups, alignment * properties and views. * * @param annotations * @param list * @param properties * @param views * @return annotation file */ public String printAnnotations(AlignmentAnnotation[] annotations, List list, Hashtable properties, HiddenColumns cs, AlignmentI al, ViewDef view) { if (view != null) { if (view.viewname != null) { text.append("VIEW_DEF\t" + view.viewname + "\n"); } if (list == null) { // list = view.visibleGroups; } if (cs == null) { cs = view.hiddencols; } if (al == null) { // add hidden rep sequences. } } // first target - store and restore all settings for a view. if (al != null && al.hasSeqrep()) { text.append("VIEW_SETREF\t" + al.getSeqrep().getName() + "\n"); } if (cs != null && cs.hasHiddenColumns()) { text.append("VIEW_HIDECOLS\t"); String regions = cs.regionsToString(",", "-"); text.append(regions); text.append("\n"); } // TODO: allow efficient recovery of annotation data shown in several // different views if (annotations != null) { boolean oneColour = true; AlignmentAnnotation row; String comma; SequenceI refSeq = null; SequenceGroup refGroup = null; StringBuffer colours = new StringBuffer(); StringBuffer graphLine = new StringBuffer(); StringBuffer rowprops = new StringBuffer(); Hashtable graphGroup = new Hashtable<>(); Hashtable graphGroup_refs = new Hashtable<>(); BitSet graphGroupSeen = new BitSet(); java.awt.Color color; for (int i = 0; i < annotations.length; i++) { row = annotations[i]; if (!row.visible && !row.hasScore() && !(row.graphGroup > -1 && graphGroupSeen.get(row.graphGroup))) { continue; } color = null; oneColour = true; // mark any sequence references for the row writeSequence_Ref(refSeq, row.sequenceRef); refSeq = row.sequenceRef; // mark any group references for the row writeGroup_Ref(refGroup, row.groupRef); refGroup = row.groupRef; boolean hasGlyphs = row.hasIcons, hasLabels = row.hasText, hasValues = row.hasScore, hasText = false; // lookahead to check what the annotation row object actually contains. for (int j = 0; row.annotations != null && j < row.annotations.length && (!hasGlyphs || !hasLabels || !hasValues); j++) { if (row.annotations[j] != null) { hasLabels |= (row.annotations[j].displayCharacter != null && row.annotations[j].displayCharacter.length() > 0 && !row.annotations[j].displayCharacter.equals(" ")); hasGlyphs |= (row.annotations[j].secondaryStructure != 0 && row.annotations[j].secondaryStructure != ' '); hasValues |= (!Float.isNaN(row.annotations[j].value)); // NaNs can't // be // rendered.. hasText |= (row.annotations[j].description != null && row.annotations[j].description.length() > 0); } } if (row.graph == AlignmentAnnotation.NO_GRAPH) { text.append("NO_GRAPH\t"); hasValues = false; // only secondary structure // hasLabels = false; // and annotation description string. } else { if (row.graph == AlignmentAnnotation.BAR_GRAPH) { text.append("BAR_GRAPH\t"); hasGlyphs = false; // no secondary structure } else if (row.graph == AlignmentAnnotation.LINE_GRAPH) { hasGlyphs = false; // no secondary structure text.append("LINE_GRAPH\t"); } if (row.getThreshold() != null) { graphLine.append("GRAPHLINE\t"); graphLine.append(row.label); graphLine.append("\t"); graphLine.append(row.getThreshold().value); graphLine.append("\t"); graphLine.append(row.getThreshold().label); graphLine.append("\t"); graphLine.append(jalview.util.Format .getHexString(row.getThreshold().colour)); graphLine.append(newline); } if (row.graphGroup > -1) { graphGroupSeen.set(row.graphGroup); Integer key = Integer.valueOf(row.graphGroup); if (graphGroup.containsKey(key)) { graphGroup.put(key, graphGroup.get(key) + "\t" + row.label); } else { graphGroup_refs.put(key, new Object[] { refSeq, refGroup }); graphGroup.put(key, row.label); } } } text.append(row.label + "\t"); if (row.description != null) { text.append(row.description + "\t"); } for (int j = 0; row.annotations != null && j < row.annotations.length; j++) { if (refSeq != null && jalview.util.Comparison.isGap(refSeq.getCharAt(j))) { continue; } if (row.annotations[j] != null) { comma = ""; if (hasGlyphs) // could be also hasGlyphs || ... { text.append(comma); if (row.annotations[j].secondaryStructure != ' ') { // only write out the field if its not whitespace. text.append(row.annotations[j].secondaryStructure); } comma = ","; } if (hasValues) { if (!Float.isNaN(row.annotations[j].value)) { text.append(comma + row.annotations[j].value); } else { // jalview.bin.Console.errPrintln("Skipping NaN - not valid value."); text.append(comma + 0f);// row.annotations[j].value); } comma = ","; } if (hasLabels) { // TODO: labels are emitted after values for bar graphs. if // empty labels are allowed, so (row.annotations[j].displayCharacter != null && row.annotations[j].displayCharacter.length() > 0 && !row.annotations[j].displayCharacter.equals(" ")) { text.append(comma + row.annotations[j].displayCharacter); comma = ","; } } if (hasText) { if (row.annotations[j].description != null && row.annotations[j].description.length() > 0 && !row.annotations[j].description .equals(row.annotations[j].displayCharacter)) { text.append(comma + row.annotations[j].description); comma = ","; } } if (color != null && !color.equals(row.annotations[j].colour)) { oneColour = false; } color = row.annotations[j].colour; if (row.annotations[j].colour != null && row.annotations[j].colour != java.awt.Color.black) { text.append(comma + "[" + jalview.util.Format .getHexString(row.annotations[j].colour) + "]"); comma = ","; } } text.append("|"); } if (row.hasScore()) { text.append("\t" + row.score); } text.append(newline); if (color != null && color != java.awt.Color.black && oneColour) { colours.append("COLOUR\t"); colours.append(row.label); colours.append("\t"); colours.append(jalview.util.Format.getHexString(color)); colours.append(newline); } if (row.scaleColLabel || row.showAllColLabels || row.centreColLabels) { rowprops.append("ROWPROPERTIES\t"); rowprops.append(row.label); rowprops.append("\tscaletofit="); rowprops.append(row.scaleColLabel); rowprops.append("\tshowalllabs="); rowprops.append(row.showAllColLabels); rowprops.append("\tcentrelabs="); rowprops.append(row.centreColLabels); rowprops.append(newline); } if (graphLine.length() > 0) { text.append(graphLine.toString()); graphLine.setLength(0); } } text.append(newline); text.append(colours.toString()); if (graphGroup.size() > 0) { SequenceI oldRefSeq = refSeq; SequenceGroup oldRefGroup = refGroup; for (Map.Entry combine_statement : graphGroup .entrySet()) { Object[] seqRefAndGroup = graphGroup_refs .get(combine_statement.getKey()); writeSequence_Ref(refSeq, (SequenceI) seqRefAndGroup[0]); refSeq = (SequenceI) seqRefAndGroup[0]; writeGroup_Ref(refGroup, (SequenceGroup) seqRefAndGroup[1]); refGroup = (SequenceGroup) seqRefAndGroup[1]; text.append("COMBINE\t"); text.append(combine_statement.getValue()); text.append(newline); } writeSequence_Ref(refSeq, oldRefSeq); refSeq = oldRefSeq; writeGroup_Ref(refGroup, oldRefGroup); refGroup = oldRefGroup; } text.append(rowprops.toString()); } if (list != null) { printGroups(list); } if (properties != null) { text.append(newline); text.append(newline); text.append("ALIGNMENT"); Enumeration en = properties.keys(); while (en.hasMoreElements()) { String key = en.nextElement().toString(); text.append("\t"); text.append(key); text.append("="); text.append(properties.get(key)); } // TODO: output alignment visualization settings here if required // iterate through one or more views, defining, marking columns and rows // as visible/hidden, and emmitting view properties. // View specific annotation is } return text.toString(); } private Object writeGroup_Ref(SequenceGroup refGroup, SequenceGroup next_refGroup) { if (next_refGroup == null) { if (refGroup != null) { text.append(newline); text.append("GROUP_REF\t"); text.append("ALIGNMENT"); text.append(newline); } return true; } else { if (refGroup == null || refGroup != next_refGroup) { text.append(newline); text.append("GROUP_REF\t"); text.append(next_refGroup.getName()); text.append(newline); return true; } } return false; } private boolean writeSequence_Ref(SequenceI refSeq, SequenceI next_refSeq) { if (next_refSeq == null) { if (refSeq != null) { text.append(newline); text.append("SEQUENCE_REF\t"); text.append("ALIGNMENT"); text.append(newline); return true; } } else { if (refSeq == null || refSeq != next_refSeq) { text.append(newline); text.append("SEQUENCE_REF\t"); text.append(next_refSeq.getName()); text.append(newline); return true; } } return false; } protected void printGroups(List list) { SequenceI seqrep = null; for (SequenceGroup sg : list) { if (!sg.hasSeqrep()) { text.append("SEQUENCE_GROUP\t" + sg.getName() + "\t" + (sg.getStartRes() + 1) + "\t" + (sg.getEndRes() + 1) + "\t" + "-1\t"); seqrep = null; } else { seqrep = sg.getSeqrep(); text.append("SEQUENCE_REF\t"); text.append(seqrep.getName()); text.append(newline); text.append("SEQUENCE_GROUP\t"); text.append(sg.getName()); text.append("\t"); text.append((seqrep.findPosition(sg.getStartRes()))); text.append("\t"); text.append((seqrep.findPosition(sg.getEndRes()))); text.append("\t"); text.append("-1\t"); } for (int s = 0; s < sg.getSize(); s++) { text.append(sg.getSequenceAt(s).getName()); text.append("\t"); } text.append(newline); text.append("PROPERTIES\t"); text.append(sg.getName()); text.append("\t"); if (sg.getDescription() != null) { text.append("description="); text.append(sg.getDescription()); text.append("\t"); } if (sg.cs != null) { text.append("colour="); text.append(ColourSchemeProperty .getColourName(sg.cs.getColourScheme())); text.append("\t"); if (sg.cs.getThreshold() != 0) { text.append("pidThreshold="); text.append(sg.cs.getThreshold()); } if (sg.cs.conservationApplied()) { text.append("consThreshold="); text.append(sg.cs.getConservationInc()); text.append("\t"); } } text.append("outlineColour="); text.append(jalview.util.Format.getHexString(sg.getOutlineColour())); text.append("\t"); text.append("displayBoxes="); text.append(sg.getDisplayBoxes()); text.append("\t"); text.append("displayText="); text.append(sg.getDisplayText()); text.append("\t"); text.append("colourText="); text.append(sg.getColourText()); text.append("\t"); text.append("showUnconserved="); text.append(sg.getShowNonconserved()); text.append("\t"); if (sg.textColour != java.awt.Color.black) { text.append("textCol1="); text.append(jalview.util.Format.getHexString(sg.textColour)); text.append("\t"); } if (sg.textColour2 != java.awt.Color.white) { text.append("textCol2="); text.append(jalview.util.Format.getHexString(sg.textColour2)); text.append("\t"); } if (sg.thresholdTextColour != 0) { text.append("textColThreshold="); text.append(sg.thresholdTextColour); text.append("\t"); } if (sg.idColour != null) { text.append("idColour="); text.append(jalview.util.Format.getHexString(sg.idColour)); text.append("\t"); } if (sg.isHidereps()) { text.append("hide=true\t"); } if (sg.isHideCols()) { text.append("hidecols=true\t"); } if (seqrep != null) { // terminate the last line and clear the sequence ref for the group text.append(newline); text.append("SEQUENCE_REF"); } text.append(newline); text.append(newline); } } public boolean annotateAlignmentView(AlignViewportI viewport, Object file, DataSourceType protocol) { ColumnSelection colSel = viewport.getColumnSelection(); HiddenColumns hidden = viewport.getAlignment().getHiddenColumns(); if (colSel == null) { colSel = new ColumnSelection(); } if (hidden == null) { hidden = new HiddenColumns(); } boolean rslt = readAnnotationFile(viewport.getAlignment(), hidden, file, protocol); if (rslt && (colSel.hasSelectedColumns() || hidden.hasHiddenColumns())) { viewport.setColumnSelection(colSel); viewport.getAlignment().setHiddenColumns(hidden); } return rslt; } public boolean readAnnotationFile(AlignmentI al, String file, DataSourceType sourceType) { return readAnnotationFile(al, null, file, sourceType); } public boolean readAnnotationFile(AlignmentI al, HiddenColumns hidden, Object file, DataSourceType sourceType) { BufferedReader in = null; try { in = new FileParse().getBufferedReader(file, sourceType); if (in != null) { return parseAnnotationFrom(al, hidden, in); } } catch (Exception ex) { ex.printStackTrace(); jalview.bin.Console.outPrintln("Problem reading annotation file: " + ex); if (nlinesread > 0) { jalview.bin.Console.outPrintln("Last read line " + nlinesread + ": '" + lastread + "' (first 80 chars) ..."); } return false; } return false; } public boolean parseAnnotationFrom(AlignmentI al, HiddenColumns hidden, BufferedReader in) throws Exception { nlinesread = 0; ArrayList combineAnnotation_calls = new ArrayList<>(); ArrayList deferredAnnotation_calls = new ArrayList<>(); boolean modified = false; String groupRef = null; Hashtable groupRefRows = new Hashtable(); Hashtable autoAnnots = new Hashtable(); { String line, label, description, token; int graphStyle, index; int refSeqIndex = 1; int existingAnnotations = 0; // when true - will add new rows regardless of whether they are duplicate // auto-annotation like consensus or conservation graphs boolean overrideAutoAnnot = false; if (al.getAlignmentAnnotation() != null) { existingAnnotations = al.getAlignmentAnnotation().length; if (existingAnnotations > 0) { AlignmentAnnotation[] aa = al.getAlignmentAnnotation(); for (int aai = 0; aai < aa.length; aai++) { if (aa[aai].autoCalculated) { // make a note of the name and description autoAnnots.put( autoAnnotsKey(aa[aai], aa[aai].sequenceRef, (aa[aai].groupRef == null ? null : aa[aai].groupRef.getName())), Integer.valueOf(1)); } } } } int alWidth = al.getWidth(); StringTokenizer st; Annotation[] annotations; AlignmentAnnotation annotation = null; // First confirm this is an Annotation file boolean jvAnnotationFile = false; while ((line = in.readLine()) != null) { nlinesread++; lastread = new String(line); if (line.indexOf("#") == 0) { continue; } if (line.indexOf("JALVIEW_ANNOTATION") > -1) { jvAnnotationFile = true; break; } } if (!jvAnnotationFile) { in.close(); return false; } while ((line = in.readLine()) != null) { nlinesread++; lastread = new String(line); if (line.indexOf("#") == 0 || line.indexOf("JALVIEW_ANNOTATION") > -1 || line.length() == 0) { continue; } st = new StringTokenizer(line, "\t"); token = st.nextToken(); if (token.equalsIgnoreCase("COLOUR")) { // TODO: use graduated colour def'n here too colourAnnotations(al, st.nextToken(), st.nextToken()); modified = true; continue; } else if (token.equalsIgnoreCase(COMBINE)) { // keep a record of current state and resolve groupRef at end combineAnnotation_calls .add(new Object[] { st, refSeq, groupRef }); modified = true; continue; } else if (token.equalsIgnoreCase("ROWPROPERTIES")) { addRowProperties(al, st); modified = true; continue; } else if (token.equalsIgnoreCase(GRAPHLINE)) { // resolve at end deferredAnnotation_calls .add(new Object[] { GRAPHLINE, st, refSeq, groupRef }); modified = true; continue; } else if (token.equalsIgnoreCase("SEQUENCE_REF")) { if (st.hasMoreTokens()) { refSeq = al.findName(refSeqId = st.nextToken()); if (refSeq == null) { refSeqId = null; } try { refSeqIndex = Integer.parseInt(st.nextToken()); if (refSeqIndex < 1) { refSeqIndex = 1; jalview.bin.Console.outPrintln( "WARNING: SEQUENCE_REF index must be > 0 in AnnotationFile"); } } catch (Exception ex) { refSeqIndex = 1; } } else { refSeq = null; refSeqId = null; } continue; } else if (token.equalsIgnoreCase("GROUP_REF")) { // Group references could be forward or backwards, so they are // resolved after the whole file is read in groupRef = null; if (st.hasMoreTokens()) { groupRef = st.nextToken(); if (groupRef.length() < 1) { groupRef = null; // empty string } else { if (groupRefRows.get(groupRef) == null) { groupRefRows.put(groupRef, new Vector()); } } } continue; } else if (token.equalsIgnoreCase("SEQUENCE_GROUP")) { addGroup(al, st); modified = true; continue; } else if (token.equalsIgnoreCase("PROPERTIES")) { addProperties(al, st); modified = true; continue; } else if (token.equalsIgnoreCase("BELOW_ALIGNMENT")) { setBelowAlignment(al, st); modified = true; continue; } else if (token.equalsIgnoreCase("ALIGNMENT")) { addAlignmentDetails(al, st); modified = true; continue; } // else if (token.equalsIgnoreCase("VIEW_DEF")) // { // addOrSetView(al,st); // modified = true; // continue; // } else if (token.equalsIgnoreCase("VIEW_SETREF")) { if (refSeq != null) { al.setSeqrep(refSeq); } modified = true; continue; } else if (token.equalsIgnoreCase("VIEW_HIDECOLS")) { if (st.hasMoreTokens()) { if (hidden == null) { hidden = new HiddenColumns(); } parseHideCols(hidden, st.nextToken()); } modified = true; continue; } else if (token.equalsIgnoreCase("HIDE_INSERTIONS")) { SequenceI sr = refSeq == null ? al.getSeqrep() : refSeq; if (sr == null) { sr = al.getSequenceAt(0); } if (sr != null) { if (hidden == null) { jalview.bin.Console.errPrintln( "Cannot process HIDE_INSERTIONS without an alignment view: Ignoring line: " + line); } else { // consider deferring this till after the file has been parsed ? hidden.hideList(sr.getInsertions()); } } modified = true; continue; } // Parse out the annotation row graphStyle = AlignmentAnnotation.getGraphValueFromString(token); label = st.nextToken(); index = 0; annotations = new Annotation[alWidth]; description = null; float score = Float.NaN; if (st.hasMoreTokens()) { line = st.nextToken(); if (line.indexOf("|") == -1) { description = line; if (st.hasMoreTokens()) { line = st.nextToken(); } } if (st.hasMoreTokens()) { // This must be the score score = Float.valueOf(st.nextToken()).floatValue(); } st = new StringTokenizer(line, "|", true); boolean emptyColumn = true; boolean onlyOneElement = (st.countTokens() == 1); while (st.hasMoreElements() && index < alWidth) { token = st.nextToken().trim(); if (onlyOneElement) { try { score = Float.valueOf(token).floatValue(); break; } catch (NumberFormatException ex) { } } if (token.equals("|")) { if (emptyColumn) { index++; } emptyColumn = true; } else { annotations[index++] = parseAnnotation(token, graphStyle); emptyColumn = false; } } } annotation = new AlignmentAnnotation(label, description, (index == 0) ? null : annotations, 0, 0, graphStyle); annotation.score = score; if (!overrideAutoAnnot && autoAnnots .containsKey(autoAnnotsKey(annotation, refSeq, groupRef))) { // skip - we've already got an automatic annotation of this type. continue; } // otherwise add it! if (refSeq != null) { annotation.belowAlignment = false; // make a copy of refSeq so we can find other matches in the alignment SequenceI referedSeq = refSeq; do { // copy before we do any mapping business. // TODO: verify that undo/redo with 1:many sequence associated // annotations can be undone correctly AlignmentAnnotation ann = new AlignmentAnnotation(annotation); annotation.createSequenceMapping(referedSeq, refSeqIndex, false); annotation.adjustForAlignment(); referedSeq.addAlignmentAnnotation(annotation); al.addAnnotation(annotation); al.setAnnotationIndex(annotation, al.getAlignmentAnnotation().length - existingAnnotations - 1); if (groupRef != null) { ((Vector) groupRefRows.get(groupRef)).addElement(annotation); } // and recover our virgin copy to use again if necessary. annotation = ann; } while (refSeqId != null && (referedSeq = al.findName(referedSeq, refSeqId, true)) != null); } else { al.addAnnotation(annotation); al.setAnnotationIndex(annotation, al.getAlignmentAnnotation().length - existingAnnotations - 1); if (groupRef != null) { ((Vector) groupRefRows.get(groupRef)).addElement(annotation); } } // and set modification flag modified = true; } // Resolve the groupRefs Hashtable groupRefLookup = new Hashtable<>(); Enumeration en = groupRefRows.keys(); while (en.hasMoreElements()) { groupRef = (String) en.nextElement(); boolean matched = false; // Resolve group: TODO: add a getGroupByName method to alignments for (SequenceGroup theGroup : al.getGroups()) { if (theGroup.getName().equals(groupRef)) { if (matched) { // TODO: specify and implement duplication of alignment annotation // for multiple group references. jalview.bin.Console.errPrintln( "Ignoring 1:many group reference mappings for group name '" + groupRef + "'"); } else { matched = true; Vector rowset = (Vector) groupRefRows.get(groupRef); groupRefLookup.put(groupRef, theGroup); if (rowset != null && rowset.size() > 0) { AlignmentAnnotation alan = null; for (int elm = 0, elmSize = rowset .size(); elm < elmSize; elm++) { alan = (AlignmentAnnotation) rowset.elementAt(elm); alan.groupRef = theGroup; } } } } } ((Vector) groupRefRows.get(groupRef)).removeAllElements(); } // process any deferred attribute settings for each context for (Object[] _deferred_args : deferredAnnotation_calls) { if (_deferred_args[0] == GRAPHLINE) { addLine(al, (StringTokenizer) _deferred_args[1], // st (SequenceI) _deferred_args[2], // refSeq (_deferred_args[3] == null) ? null : groupRefLookup.get(_deferred_args[3]) // the // reference // group, or // null ); } } // finally, combine all the annotation rows within each context. /** * number of combine statements in this annotation file. Used to create * new groups for combined annotation graphs without disturbing existing * ones */ int combinecount = 0; for (Object[] _combine_args : combineAnnotation_calls) { combineAnnotations(al, ++combinecount, (StringTokenizer) _combine_args[0], // st (SequenceI) _combine_args[1], // refSeq (_combine_args[2] == null) ? null : groupRefLookup.get(_combine_args[2]) // the reference // group, // or null ); } } return modified; } private void parseHideCols(HiddenColumns hidden, String nextToken) { StringTokenizer inval = new StringTokenizer(nextToken, ","); while (inval.hasMoreTokens()) { String range = inval.nextToken().trim(); int from, to = range.indexOf("-"); if (to == -1) { from = to = Integer.parseInt(range); if (from >= 0) { hidden.hideColumns(from, to); } } else { from = Integer.parseInt(range.substring(0, to)); if (to < range.length() - 1) { to = Integer.parseInt(range.substring(to + 1)); } else { to = from; } if (from > 0 && to >= from) { hidden.hideColumns(from, to); } } } } private Object autoAnnotsKey(AlignmentAnnotation annotation, SequenceI refSeq, String groupRef) { return annotation.graph + "\t" + annotation.label + "\t" + annotation.description + "\t" + (refSeq != null ? refSeq.getDisplayId(true) : ""); } Annotation parseAnnotation(String string, int graphStyle) { // don't do the glyph test if we don't want secondary structure boolean hasSymbols = (graphStyle == AlignmentAnnotation.NO_GRAPH); String desc = null, displayChar = null; char ss = ' '; // secondaryStructure float value = 0; boolean parsedValue = false, dcset = false; // find colour here Color colour = null; int i = string.indexOf("["); int j = string.indexOf("]"); if (i > -1 && j > -1) { colour = ColorUtils.parseColourString(string.substring(i + 1, j)); if (i > 0 && string.charAt(i - 1) == ',') { // clip the preceding comma as well i--; } string = string.substring(0, i) + string.substring(j + 1); } StringTokenizer st = new StringTokenizer(string, ",", true); String token; boolean seenContent = false; int pass = 0; while (st.hasMoreTokens()) { pass++; token = st.nextToken().trim(); if (token.equals(",")) { if (!seenContent && parsedValue && !dcset) { // allow the value below the bar/line to be empty dcset = true; displayChar = " "; } seenContent = false; continue; } else { seenContent = true; } if (!parsedValue) { try { displayChar = token; // foo value = Float.valueOf(token).floatValue(); parsedValue = true; continue; } catch (NumberFormatException ex) { } } else { if (token.length() == 1) { displayChar = token; } } if (hasSymbols && (token.length() == 1 && "()<>[]{}AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" .contains(token))) { // Either this character represents a helix or sheet // or an integer which can be displayed ss = token.charAt(0); if (displayChar.equals(token.substring(0, 1))) { displayChar = ""; } } else if (desc == null || (parsedValue && pass > 2)) { desc = token; } } // if (!dcset && string.charAt(string.length() - 1) == ',') // { // displayChar = " "; // empty display char symbol. // } if (displayChar != null && desc != null && desc.length() == 1) { if (displayChar.length() > 1) { // switch desc and displayChar - legacy support String tmp = displayChar; displayChar = desc; desc = tmp; } else { if (displayChar.equals(desc)) { // duplicate label - hangover from the 'robust parser' above desc = null; } } } Annotation anot = new Annotation(displayChar, desc, ss, value); anot.colour = colour; return anot; } void colourAnnotations(AlignmentI al, String label, String colour) { Color awtColour = ColorUtils.parseColourString(colour); Annotation[] annotations; for (int i = 0; i < al.getAlignmentAnnotation().length; i++) { if (al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(label)) { annotations = al.getAlignmentAnnotation()[i].annotations; for (int j = 0; j < annotations.length; j++) { if (annotations[j] != null) { annotations[j].colour = awtColour; } } } } } void combineAnnotations(AlignmentI al, int combineCount, StringTokenizer st, SequenceI seqRef, SequenceGroup groupRef) { String group = st.nextToken(); // First make sure we are not overwriting the graphIndex int graphGroup = 0; if (al.getAlignmentAnnotation() != null) { for (int i = 0; i < al.getAlignmentAnnotation().length; i++) { AlignmentAnnotation aa = al.getAlignmentAnnotation()[i]; if (aa.graphGroup > graphGroup) { // try to number graphGroups in order of occurence. graphGroup = aa.graphGroup + 1; } if (aa.sequenceRef == seqRef && aa.groupRef == groupRef && aa.label.equalsIgnoreCase(group)) { if (aa.graphGroup > -1) { graphGroup = aa.graphGroup; } else { if (graphGroup <= combineCount) { graphGroup = combineCount + 1; } aa.graphGroup = graphGroup; } break; } } // Now update groups while (st.hasMoreTokens()) { group = st.nextToken(); for (int i = 0; i < al.getAlignmentAnnotation().length; i++) { AlignmentAnnotation aa = al.getAlignmentAnnotation()[i]; if (aa.sequenceRef == seqRef && aa.groupRef == groupRef && aa.label.equalsIgnoreCase(group)) { aa.graphGroup = graphGroup; break; } } } } else { jalview.bin.Console.errPrintln( "Couldn't combine annotations. None are added to alignment yet!"); } } void addLine(AlignmentI al, StringTokenizer st, SequenceI seqRef, SequenceGroup groupRef) { String group = st.nextToken(); AlignmentAnnotation[] alannot = al.getAlignmentAnnotation(); String nextToken = st.nextToken(); float value = 0f; try { value = Float.valueOf(nextToken); } catch (NumberFormatException e) { jalview.bin.Console.errPrintln("line " + nlinesread + ": Threshold '" + nextToken + "' invalid, setting to zero"); } String label = st.hasMoreTokens() ? st.nextToken() : null; Color colour = null; if (st.hasMoreTokens()) { colour = ColorUtils.parseColourString(st.nextToken()); } if (alannot != null) { for (int i = 0; i < alannot.length; i++) { if (alannot[i].label.equalsIgnoreCase(group) && (seqRef == null || alannot[i].sequenceRef == seqRef) && (groupRef == null || alannot[i].groupRef == groupRef)) { alannot[i].setThreshold(new GraphLine(value, label, colour)); } } } } void addGroup(AlignmentI al, StringTokenizer st) { SequenceGroup sg = new SequenceGroup(); sg.setName(st.nextToken()); String rng = ""; try { rng = st.nextToken(); if (rng.length() > 0 && !rng.startsWith("*")) { sg.setStartRes(Integer.parseInt(rng) - 1); } else { sg.setStartRes(0); } rng = st.nextToken(); if (rng.length() > 0 && !rng.startsWith("*")) { sg.setEndRes(Integer.parseInt(rng) - 1); } else { sg.setEndRes(al.getWidth() - 1); } } catch (Exception e) { jalview.bin.Console.errPrintln( "Couldn't parse Group Start or End Field as '*' or a valid column or sequence index: '" + rng + "' - assuming alignment width for group."); // assume group is full width sg.setStartRes(0); sg.setEndRes(al.getWidth() - 1); } String index = st.nextToken(); if (index.equals("-1")) { while (st.hasMoreElements()) { sg.addSequence(al.findName(st.nextToken()), false); } } else { StringTokenizer st2 = new StringTokenizer(index, ","); while (st2.hasMoreTokens()) { String tmp = st2.nextToken(); if (tmp.equals("*")) { for (int i = 0; i < al.getHeight(); i++) { sg.addSequence(al.getSequenceAt(i), false); } } else if (tmp.indexOf("-") >= 0) { StringTokenizer st3 = new StringTokenizer(tmp, "-"); int start = (Integer.parseInt(st3.nextToken())); int end = (Integer.parseInt(st3.nextToken())); if (end > start) { for (int i = start; i <= end; i++) { sg.addSequence(al.getSequenceAt(i - 1), false); } } } else { sg.addSequence(al.getSequenceAt(Integer.parseInt(tmp) - 1), false); } } } if (refSeq != null) { sg.setStartRes(refSeq.findIndex(sg.getStartRes() + 1) - 1); sg.setEndRes(refSeq.findIndex(sg.getEndRes() + 1) - 1); sg.setSeqrep(refSeq); } if (sg.getSize() > 0) { al.addGroup(sg); } } void addRowProperties(AlignmentI al, StringTokenizer st) { String label = st.nextToken(), keyValue, key, value; boolean scaletofit = false, centerlab = false, showalllabs = false; while (st.hasMoreTokens()) { keyValue = st.nextToken(); key = keyValue.substring(0, keyValue.indexOf("=")); value = keyValue.substring(keyValue.indexOf("=") + 1); if (key.equalsIgnoreCase("scaletofit")) { scaletofit = Boolean.valueOf(value).booleanValue(); } if (key.equalsIgnoreCase("showalllabs")) { showalllabs = Boolean.valueOf(value).booleanValue(); } if (key.equalsIgnoreCase("centrelabs")) { centerlab = Boolean.valueOf(value).booleanValue(); } AlignmentAnnotation[] alr = al.getAlignmentAnnotation(); if (alr != null) { for (int i = 0; i < alr.length; i++) { if (alr[i].label.equalsIgnoreCase(label)) { alr[i].centreColLabels = centerlab; alr[i].scaleColLabel = scaletofit; alr[i].showAllColLabels = showalllabs; } } } } } void addProperties(AlignmentI al, StringTokenizer st) { // So far we have only added groups to the annotationHash, // the idea is in the future properties can be added to // alignments, other annotations etc if (al.getGroups() == null) { return; } String name = st.nextToken(); Map properties = new HashMap<>(); while (st.hasMoreTokens()) { String keyValue = st.nextToken(); String key = keyValue.substring(0, keyValue.indexOf("=")); String value = keyValue.substring(keyValue.indexOf("=") + 1); properties.put(key, value); } for (SequenceGroup sg : al.getGroups()) { if (sg.getName().equals(name)) { addProperties(sg, properties, al); } } } /** * Helper method that applies any specified properties to a SequenceGroup * * @param sg * @param properties * @param al */ private void addProperties(SequenceGroup sg, Map properties, AlignmentI al) { ColourSchemeI def = sg.getColourScheme(); for (String key : properties.keySet()) { String value = properties.get(key); if (key.equalsIgnoreCase("description")) { sg.setDescription(value); } else if (key.equalsIgnoreCase("colour")) { // TODO need to notify colourscheme of view reference once it is // available sg.cs.setColourScheme( ColourSchemeProperty.getColourScheme(null, al, value)); } else if (key.equalsIgnoreCase("pidThreshold")) { sg.cs.setThreshold(Integer.parseInt(value), true); } else if (key.equalsIgnoreCase("consThreshold")) { sg.cs.setConservationInc(Integer.parseInt(value)); Conservation c = new Conservation("Group", sg.getSequences(null), sg.getStartRes(), sg.getEndRes() + 1); c.calculate(); c.verdict(false, 25); // TODO: refer to conservation percent threshold sg.cs.setConservation(c); } else if (key.equalsIgnoreCase("outlineColour")) { sg.setOutlineColour(ColorUtils.parseColourString(value)); } else if (key.equalsIgnoreCase("displayBoxes")) { sg.setDisplayBoxes(Boolean.valueOf(value).booleanValue()); } else if (key.equalsIgnoreCase("showUnconserved")) { sg.setShowNonconserved(Boolean.valueOf(value).booleanValue()); } else if (key.equalsIgnoreCase("displayText")) { sg.setDisplayText(Boolean.valueOf(value).booleanValue()); } else if (key.equalsIgnoreCase("colourText")) { sg.setColourText(Boolean.valueOf(value).booleanValue()); } else if (key.equalsIgnoreCase("textCol1")) { sg.textColour = ColorUtils.parseColourString(value); } else if (key.equalsIgnoreCase("textCol2")) { sg.textColour2 = ColorUtils.parseColourString(value); } else if (key.equalsIgnoreCase("textColThreshold")) { sg.thresholdTextColour = Integer.parseInt(value); } else if (key.equalsIgnoreCase("idColour")) { Color idColour = ColorUtils.parseColourString(value); sg.setIdColour(idColour == null ? Color.black : idColour); } else if (key.equalsIgnoreCase("hide")) { // see bug https://mantis.lifesci.dundee.ac.uk/view.php?id=25847 sg.setHidereps(true); } else if (key.equalsIgnoreCase("hidecols")) { // see bug https://mantis.lifesci.dundee.ac.uk/view.php?id=25847 sg.setHideCols(true); } sg.recalcConservation(); } if (sg.getColourScheme() == null) { sg.setColourScheme(def); } } void setBelowAlignment(AlignmentI al, StringTokenizer st) { String token; AlignmentAnnotation aa, ala[] = al.getAlignmentAnnotation(); if (ala == null) { System.err.print( "Warning - no annotation to set below for sequence associated annotation:"); } while (st.hasMoreTokens()) { token = st.nextToken(); if (ala == null) { System.err.print(" " + token); } else { for (int i = 0; i < al.getAlignmentAnnotation().length; i++) { aa = al.getAlignmentAnnotation()[i]; if (aa.sequenceRef == refSeq && aa.label.equals(token)) { aa.belowAlignment = true; } } } } if (ala == null) { System.err.print("\n"); } } void addAlignmentDetails(AlignmentI al, StringTokenizer st) { String keyValue, key, value; while (st.hasMoreTokens()) { keyValue = st.nextToken(); key = keyValue.substring(0, keyValue.indexOf("=")); value = keyValue.substring(keyValue.indexOf("=") + 1); al.setProperty(key, value); } } /** * Write annotations as a CSV file of the form 'label, value, value, ...' for * each row. * * @param annotations * @return CSV file as a string. */ public String printCSVAnnotations(AlignmentAnnotation[] annotations) { if (annotations == null) { return ""; } StringBuffer sp = new StringBuffer(); for (int i = 0; i < annotations.length; i++) { String atos = annotations[i].toString(); int p = 0; do { int cp = atos.indexOf("\n", p); sp.append(annotations[i].label); sp.append(","); if (cp > p) { sp.append(atos.substring(p, cp + 1)); } else { sp.append(atos.substring(p)); sp.append(newline); } p = cp + 1; } while (p > 0); } return sp.toString(); } public String printAnnotationsForView(AlignViewportI viewport) { return printAnnotations( viewport.isShowAnnotation() ? viewport.getAlignment().getAlignmentAnnotation() : null, viewport.getAlignment().getGroups(), viewport.getAlignment().getProperties(), viewport.getAlignment().getHiddenColumns(), viewport.getAlignment(), null); } public String printAnnotationsForAlignment(AlignmentI al) { return printAnnotations(al.getAlignmentAnnotation(), al.getGroups(), al.getProperties(), null, al, null); } }