X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fio%2FFeaturesFile.java;h=1afe4c4e6e388c0a8a54d1dd052d52f3b86cc8cb;hb=56fd15ca5a6e0dd96930474d1fb02b3c5fb0f00f;hp=3c8ad21b2978de6d84dd414bfc3375bc520771ad;hpb=336b270e68fe281a9a881f751fe850e8a92e05c9;p=jalview.git diff --git a/src/jalview/io/FeaturesFile.java b/src/jalview/io/FeaturesFile.java index 3c8ad21..1afe4c4 100755 --- a/src/jalview/io/FeaturesFile.java +++ b/src/jalview/io/FeaturesFile.java @@ -1,17 +1,17 @@ /* - * Jalview - A Sequence Alignment Editor and Viewer - * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle - * + * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1) + * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA @@ -23,19 +23,26 @@ import java.util.*; import jalview.datamodel.*; import jalview.schemes.*; +import jalview.util.Format; /** - * Parse and create Jalview Features files - * Detects GFF format features files and parses. - * Does not implement standard print() - call specific printFeatures or printGFF. - * Uses AlignmentI.findSequence(String id) to find the sequence object for the features annotation - this normally works on an exact match. + * Parse and create Jalview Features files Detects GFF format features files and + * parses. Does not implement standard print() - call specific printFeatures or + * printGFF. Uses AlignmentI.findSequence(String id) to find the sequence object + * for the features annotation - this normally works on an exact match. + * * @author AMW * @version $Revision$ */ -public class FeaturesFile - extends AlignFile +public class FeaturesFile extends AlignFile { /** + * work around for GFF interpretation bug where source string becomes + * description rather than a group + */ + private boolean doGffSource = true; + + /** * Creates a new FeaturesFile object. */ public FeaturesFile() @@ -44,43 +51,47 @@ public class FeaturesFile /** * Creates a new FeaturesFile object. - * - * @param inFile DOCUMENT ME! - * @param type DOCUMENT ME! - * - * @throws IOException DOCUMENT ME! + * + * @param inFile + * DOCUMENT ME! + * @param type + * DOCUMENT ME! + * + * @throws IOException + * DOCUMENT ME! */ - public FeaturesFile(String inFile, String type) - throws IOException + public FeaturesFile(String inFile, String type) throws IOException { super(inFile, type); } + public FeaturesFile(FileParse source) throws IOException + { + super(source); + } + /** - * The Application can render HTML, but the applet will - * remove HTML tags and replace links with %LINK% - * Both need to read links in HTML however - * - * @throws IOException DOCUMENT ME! + * The Application can render HTML, but the applet will remove HTML tags and + * replace links with %LINK% Both need to read links in HTML however + * + * @throws IOException + * DOCUMENT ME! */ - public boolean parse(AlignmentI align, - Hashtable colours, - boolean removeHTML) + public boolean parse(AlignmentI align, Hashtable colours, + boolean removeHTML) { return parse(align, colours, null, removeHTML); } /** - * The Application can render HTML, but the applet will - * remove HTML tags and replace links with %LINK% - * Both need to read links in HTML however - * - * @throws IOException DOCUMENT ME! + * The Application can render HTML, but the applet will remove HTML tags and + * replace links with %LINK% Both need to read links in HTML however + * + * @throws IOException + * DOCUMENT ME! */ - public boolean parse(AlignmentI align, - Hashtable colours, - Hashtable featureLink, - boolean removeHTML) + public boolean parse(AlignmentI align, Hashtable colours, + Hashtable featureLink, boolean removeHTML) { String line = null; try @@ -97,7 +108,7 @@ public class FeaturesFile boolean GFFFile = true; - while ( (line = nextLine()) != null) + while ((line = nextLine()) != null) { if (line.startsWith("#")) { @@ -120,16 +131,145 @@ public class FeaturesFile } else if (type.equalsIgnoreCase("endgroup")) { - //We should check whether this is the current group, - //but at present theres no way of showing more than 1 group + // We should check whether this is the current group, + // but at present theres no way of showing more than 1 group st.nextToken(); featureGroup = null; groupLink = null; } else { - UserColourScheme ucs = new UserColourScheme(st.nextToken()); - colours.put(type, ucs.findColour('A')); + Object colour = null; + String colscheme = st.nextToken(); + if (colscheme.indexOf("|") > -1) + { + // Parse '|' separated graduated colourscheme fields: + // mincolour|maxcolour|[absolute|]minvalue|maxvalue|thresholdtype|thresholdvalue + // first four are required. + // first two are hexadecimal or word equivalent colours. + // second two are values parsed as floats. + // fifth is either 'above','below', or 'none'. + // sixth is a float value and only required when fifth is either + // 'above' or 'below'. + StringTokenizer gcol = new StringTokenizer(colscheme, "|"); + String mincol = gcol.nextToken(), maxcol = gcol.nextToken(); + String abso = gcol + .nextToken(), minval, maxval; + if (abso.toLowerCase().indexOf("abso")!=0) { + minval = abso; + abso = null; + } else { + minval = gcol.nextToken(); + } + maxval = gcol.nextToken(); + // set defaults + int threshtype = AnnotationColourGradient.NO_THRESHOLD; + float min=Float.MIN_VALUE,max=Float.MAX_VALUE,threshval=Float.NaN; + try + { + if (minval.length()>0) { + min = new Float(minval).floatValue(); + } + } catch (Exception e) + { + System.err + .println("Couldn't parse the minimum value for graduated colour for type (" + + colscheme + ") - did you misspell 'auto' for the optional automatic colour switch ?"); + e.printStackTrace(); + } + try + { + if (maxval.length()>0) { + max = new Float(maxval).floatValue(); + } + } catch (Exception e) + { + System.err + .println("Couldn't parse the maximum value for graduated colour for type (" + + colscheme + ")"); + e.printStackTrace(); + } + try + { + colour = new jalview.schemes.GraduatedColor( + new UserColourScheme(mincol).findColour('A'), + new UserColourScheme(maxcol).findColour('A'), + min, max); + } catch (Exception e) + { + System.err + .println("Couldn't parse the graduated colour scheme (" + + colscheme + ")"); + e.printStackTrace(); + } + if (colour != null) + { + ((jalview.schemes.GraduatedColor)colour).setAutoScaled(abso==null); + // add in any additional parameters + String ttype = null, tval = null; + if (gcol.hasMoreTokens()) + { + // threshold type and possibly a threshold value + ttype = gcol.nextToken(); + if (ttype.toLowerCase().startsWith("below")) + { + ((jalview.schemes.GraduatedColor) colour) + .setThreshType(AnnotationColourGradient.BELOW_THRESHOLD); + } + else if (ttype.toLowerCase().startsWith("above")) + { + ((jalview.schemes.GraduatedColor) colour) + .setThreshType(AnnotationColourGradient.ABOVE_THRESHOLD); + } + else + { + ((jalview.schemes.GraduatedColor) colour) + .setThreshType(AnnotationColourGradient.NO_THRESHOLD); + if (!ttype.toLowerCase().startsWith("no")) + { + System.err + .println("Ignoring unrecognised threshold type : " + + ttype); + } + } + } + if (((GraduatedColor)colour).getThreshType()!=AnnotationColourGradient.NO_THRESHOLD) + { + tval = gcol.nextToken(); + try + { + ((jalview.schemes.GraduatedColor) colour) + .setThresh(new Float(tval).floatValue()); + } catch (Exception e) + { + System.err + .println("Couldn't parse threshold value as a float: (" + + tval + ")"); + e.printStackTrace(); + } + } + // parse the thresh-is-min token ? + if (gcol.hasMoreTokens()) + { + System.err + .println("Ignoring additional tokens in parameters in graduated colour specification\n"); + while (gcol.hasMoreTokens()) + { + System.err.println("|" + gcol); + } + System.err.println("\n"); + } + } + } + else + { + UserColourScheme ucs = new UserColourScheme(colscheme); + colour = ucs.findColour('A'); + } + if (colour != null) + { + colours.put(type, colour); + } if (st.hasMoreElements()) { String link = st.nextToken(); @@ -140,11 +280,10 @@ public class FeaturesFile } featureLink.put(type, link); } - } continue; } - + String seqId = ""; while (st.hasMoreElements()) { @@ -152,43 +291,72 @@ public class FeaturesFile { // Still possible this is an old Jalview file, // which does not have type colours at the beginning - token = st.nextToken(); - seq = align.findName(token); + seqId = token = st.nextToken(); + seq = align.findName(seqId, true); if (seq != null) { desc = st.nextToken(); + String group = null; + if (doGffSource && desc.indexOf(' ') == -1) + { + // could also be a source term rather than description line + group = new String(desc); + } type = st.nextToken(); - try { - start = Integer.parseInt(st.nextToken()); + try + { + String stt = st.nextToken(); + if (stt.length() == 0 || stt.equals("-")) + { + start = 0; + } + else + { + start = Integer.parseInt(stt); + } } catch (NumberFormatException ex) { - start=0; + start = 0; } - try { - end = Integer.parseInt(st.nextToken()); + try + { + String stt = st.nextToken(); + if (stt.length() == 0 || stt.equals("-")) + { + end = 0; + } + else + { + end = Integer.parseInt(stt); + } + } catch (NumberFormatException ex) + { + end = 0; } - catch (NumberFormatException ex) + // TODO: decide if non positional feature assertion for input data + // where end==0 is generally valid + if (end == 0) { - end=-1; - } + // treat as non-positional feature, regardless. + start = 0; + } try { score = new Float(st.nextToken()).floatValue(); - } - catch (NumberFormatException ex) + } catch (NumberFormatException ex) { score = 0; } - sf = new SequenceFeature(type, desc, start, end, score, null); + sf = new SequenceFeature(type, desc, start, end, score, group); try { sf.setValue("STRAND", st.nextToken()); sf.setValue("FRAME", st.nextToken()); + } catch (Exception ex) + { } - catch (Exception ex) - {} if (st.hasMoreTokens()) { @@ -197,11 +365,17 @@ public class FeaturesFile { attributes.append("\t" + st.nextElement()); } + // TODO validate and split GFF2 attributes field ? parse out + // ([A-Za-z][A-Za-z0-9_]*) ; and add as + // sf.setValue(attrib, val); sf.setValue("ATTRIBUTES", attributes.toString()); } seq.addSequenceFeature(sf); - + while ((seq = align.findName(seq, seqId, true)) != null) + { + seq.addSequenceFeature(new SequenceFeature(sf)); + } break; } } @@ -216,26 +390,28 @@ public class FeaturesFile } if (!st.hasMoreTokens()) { - System.err.println("DEBUG: Run out of tokens when trying to identify the destination for the feature.. giving up."); - // in all probability, this isn't a file we understand, so bail quietly. + System.err + .println("DEBUG: Run out of tokens when trying to identify the destination for the feature.. giving up."); + // in all probability, this isn't a file we understand, so bail + // quietly. return false; } - + token = st.nextToken(); - + if (!token.equals("ID_NOT_SPECIFIED")) { - seq = align.findName(token); + seq = align.findName(seqId = token, true); st.nextToken(); } else { + seqId = null; try { index = Integer.parseInt(st.nextToken()); seq = align.getSequenceAt(index); - } - catch (NumberFormatException ex) + } catch (NumberFormatException ex) { seq = null; } @@ -258,11 +434,19 @@ public class FeaturesFile UserColourScheme ucs = new UserColourScheme(type); colours.put(type, ucs.findColour('A')); } - sf = new SequenceFeature(type, desc, "", start, end, featureGroup); - - seq.addSequenceFeature(sf); - + if (st.hasMoreTokens()) + { + try + { + score = new Float(st.nextToken()).floatValue(); + // update colourgradient bounds if allowed to + } catch (NumberFormatException ex) + { + score = 0; + } + sf.setScore(score); + } if (groupLink != null && removeHTML) { sf.addLink(groupLink); @@ -276,12 +460,18 @@ public class FeaturesFile parseDescriptionHTML(sf, removeHTML); - //If we got here, its not a GFFFile + seq.addSequenceFeature(sf); + + while (seqId != null + && (seq = align.findName(seq, seqId, false)) != null) + { + seq.addSequenceFeature(new SequenceFeature(sf)); + } + // If we got here, its not a GFFFile GFFFile = false; } } - } - catch (Exception ex) + } catch (Exception ex) { System.out.println(line); System.out.println("Error parsing feature file: " + ex + "\n" + line); @@ -299,7 +489,8 @@ public class FeaturesFile return; } - if (removeHTML && sf.getDescription().toUpperCase().indexOf("") == -1) + if (removeHTML + && sf.getDescription().toUpperCase().indexOf("") == -1) { removeHTML = false; } @@ -367,40 +558,92 @@ public class FeaturesFile } /** - * DOCUMENT ME! - * - * @param s DOCUMENT ME! - * @param len DOCUMENT ME! - * @param gaps DOCUMENT ME! - * @param displayId DOCUMENT ME! - * - * @return DOCUMENT ME! + * generate a features file for seqs + * + * @param seqs + * source of sequence features + * @param visible + * hash of feature types and colours + * @return features file contents */ - public String printJalviewFormat(SequenceI[] seqs, - Hashtable visible) + public String printJalviewFormat(SequenceI[] seqs, Hashtable visible) + { + return printJalviewFormat(seqs, visible, true); + } + + /** + * generate a features file for seqs with colours from visible (if any) + * + * @param seqs + * source of features + * @param visible + * hash of Colours for each feature type + * @param visOnly + * when true only feature types in 'visible' will be output + * @return features file contents + */ + public String printJalviewFormat(SequenceI[] seqs, Hashtable visible, + boolean visOnly) { StringBuffer out = new StringBuffer(); SequenceFeature[] next; - if (visible == null || visible.size() < 1) + if (visOnly && (visible == null || visible.size() < 1)) { return "No Features Visible"; } - - Enumeration en = visible.keys(); - String type; - int color; - while (en.hasMoreElements()) + if (visible != null && visOnly) { - type = en.nextElement().toString(); - color = Integer.parseInt(visible.get(type).toString()); - out.append(type + "\t" - + jalview.util.Format.getHexString( - new java.awt.Color(color)) - + "\n"); - } + // write feature colours only if we're given them and we are generating + // viewed features + // TODO: decide if feature links should also be written here ? + Enumeration en = visible.keys(); + String type, color; + while (en.hasMoreElements()) + { + type = en.nextElement().toString(); - //Work out which groups are both present and visible + if (visible.get(type) instanceof GraduatedColor) + { + GraduatedColor gc = (GraduatedColor) visible.get(type); + color = Format.getHexString(gc.getMinColor()) + "|" + + Format.getHexString(gc.getMaxColor()) + + (gc.isAutoScale() ? "|" : "|abso|")+ gc.getMin() + "|" + gc.getMax() + "|"; + if (gc.getThreshType() != AnnotationColourGradient.NO_THRESHOLD) + { + if (gc.getThreshType() == AnnotationColourGradient.BELOW_THRESHOLD) + { + color += "below"; + } + else + { + if (gc.getThreshType() != AnnotationColourGradient.ABOVE_THRESHOLD) + { + System.err.println("WARNING: Unsupported threshold type (" + + gc.getThreshType() + ") : Assuming 'above'"); + } + color += "above"; + } + // add the value + color += "|" + gc.getThresh(); + } + else + { + color += "none"; + } + } + else + if (visible.get(type) instanceof java.awt.Color) { + color = Format.getHexString((java.awt.Color)visible.get(type)); + } else { + // legacy support for integer objects containing colour triplet values + color = Format.getHexString(new java.awt.Color(Integer + .parseInt(visible.get(type).toString()))); + } + out.append(type + "\t" + color + "\n"); + } + } + // Work out which groups are both present and visible Vector groups = new Vector(); int groupIndex = 0; @@ -411,13 +654,13 @@ public class FeaturesFile { for (int j = 0; j < next.length; j++) { - if (!visible.containsKey(next[j].type)) + if (visOnly && !visible.containsKey(next[j].type)) { continue; } if (next[j].featureGroup != null - && !groups.contains(next[j].featureGroup)) + && !groups.contains(next[j].featureGroup)) { groups.addElement(next[j].featureGroup); } @@ -447,15 +690,14 @@ public class FeaturesFile { for (int j = 0; j < next.length; j++) { - if (!visible.containsKey(next[j].type)) + if (visOnly && !visible.containsKey(next[j].type)) { continue; } if (group != null - && (next[j].featureGroup == null - || !next[j].featureGroup.equals(group)) - ) + && (next[j].featureGroup == null || !next[j].featureGroup + .equals(group))) { continue; } @@ -465,14 +707,15 @@ public class FeaturesFile continue; } - if (next[j].description == null || next[j].description.equals("")) + if (next[j].description == null + || next[j].description.equals("")) { out.append(next[j].type + "\t"); } else { if (next[j].links != null - && next[j].getDescription().indexOf("") == -1) + && next[j].getDescription().indexOf("") == -1) { out.append(""); } @@ -488,11 +731,9 @@ public class FeaturesFile if (next[j].description.indexOf(href) == -1) { - out.append("" - + label - + ""); + out + .append("" + label + + ""); } } @@ -505,11 +746,8 @@ public class FeaturesFile out.append("\t"); } - out.append(seqs[i].getName() + "\t-1\t" - + next[j].begin + "\t" - + next[j].end + "\t" - + next[j].type + "\n" - ); + out.append(seqs[i].getName() + "\t-1\t" + next[j].begin + "\t" + + next[j].end + "\t" + next[j].type + ((visible.get(next[j].type) instanceof GraduatedColor) ? "\t"+next[j].score+"\n" : "\n")); } } } @@ -524,14 +762,19 @@ public class FeaturesFile break; } - } - while (groupIndex < groups.size() + 1); + } while (groupIndex < groups.size() + 1); return out.toString(); } public String printGFFFormat(SequenceI[] seqs, Hashtable visible) { + return printGFFFormat(seqs, visible, true); + } + + public String printGFFFormat(SequenceI[] seqs, Hashtable visible, + boolean visOnly) + { StringBuffer out = new StringBuffer(); SequenceFeature[] next; String source; @@ -543,7 +786,8 @@ public class FeaturesFile next = seqs[i].getSequenceFeatures(); for (int j = 0; j < next.length; j++) { - if (!visible.containsKey(next[j].type)) + if (visOnly && visible != null + && !visible.containsKey(next[j].type)) { continue; } @@ -554,13 +798,9 @@ public class FeaturesFile source = next[j].getDescription(); } - out.append(seqs[i].getName() + "\t" - + source + "\t" - + next[j].type + "\t" - + next[j].begin + "\t" - + next[j].end + "\t" - + next[j].score + "\t" - ); + out.append(seqs[i].getName() + "\t" + source + "\t" + + next[j].type + "\t" + next[j].begin + "\t" + + next[j].end + "\t" + next[j].score + "\t"); if (next[j].getValue("STRAND") != null) { @@ -594,15 +834,18 @@ public class FeaturesFile return out.toString(); } + /** + * this is only for the benefit of object polymorphism - method does nothing. + */ public void parse() { - //IGNORED + // IGNORED } /** - * DOCUMENT ME! - * - * @return DOCUMENT ME! + * this is only for the benefit of object polymorphism - method does nothing. + * + * @return error message */ public String print() {