X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fio%2FFeaturesFile.java;h=169da5a5ae8624885d772814aa049ede2be2e4ac;hb=d92dd77fe197fb6bf7b78da41d00f1f975a41671;hp=e0722c05cc832abc81dcbc134da6954470966206;hpb=acf9118bba86938c8406e7041b4e5a507f4f37a3;p=jalview.git diff --git a/src/jalview/io/FeaturesFile.java b/src/jalview/io/FeaturesFile.java index e0722c0..169da5a 100755 --- a/src/jalview/io/FeaturesFile.java +++ b/src/jalview/io/FeaturesFile.java @@ -31,6 +31,8 @@ import jalview.datamodel.AlignmentI; 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.io.gff.GffHelperFactory; import jalview.io.gff.GffHelperI; @@ -68,6 +70,16 @@ import java.util.Map.Entry; */ public class FeaturesFile extends AlignFile implements FeaturesSourceI { + private static final String TAB_REGEX = "\\t"; + + private static final String STARTGROUP = "STARTGROUP"; + + private static final String ENDGROUP = "ENDGROUP"; + + private static final String STARTFILTERS = "STARTFILTERS"; + + private static final String ENDFILTERS = "ENDFILTERS"; + private static final String ID_NOT_SPECIFIED = "ID_NOT_SPECIFIED"; private static final String NOTE = "Note"; @@ -167,7 +179,7 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI * @param align * - alignment/dataset containing sequences that are to be annotated * @param colours - * - hashtable to store feature colour definitions + * - map to store feature colour definitions * @param removeHTML * - process html strings into plain text * @param relaxedIdmatching @@ -178,6 +190,29 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI Map colours, boolean removeHTML, boolean relaxedIdmatching) { + return parse(align, colours, null, removeHTML, relaxedIdmatching); + } + + /** + * Parse GFF or Jalview format sequence features file + * + * @param align + * - alignment/dataset containing sequences that are to be annotated + * @param colours + * - map to store feature colour definitions + * @param filters + * - map to store feature filter definitions + * @param removeHTML + * - process html strings into plain text + * @param relaxedIdmatching + * - when true, ID matches to compound sequence IDs are allowed + * @return true if features were added + */ + public boolean parse(AlignmentI align, + Map colours, + Map filters, boolean removeHTML, + boolean relaxedIdmatching) + { Map gffProps = new HashMap<>(); /* * keep track of any sequences we try to create from the data @@ -202,7 +237,7 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI continue; } - gffColumns = line.split("\\t"); // tab as regex + gffColumns = line.split(TAB_REGEX); if (gffColumns.length == 1) { if (line.trim().equalsIgnoreCase("GFF")) @@ -216,18 +251,23 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI } } - if (gffColumns.length > 1 && gffColumns.length < 4) + if (gffColumns.length > 0 && gffColumns.length < 4) { /* * if 2 or 3 tokens, we anticipate either 'startgroup', 'endgroup' or * a feature type colour specification */ String ft = gffColumns[0]; - if (ft.equalsIgnoreCase("startgroup")) + if (ft.equalsIgnoreCase(STARTFILTERS)) + { + parseFilters(filters); + continue; + } + if (ft.equalsIgnoreCase(STARTGROUP)) { featureGroup = gffColumns[1]; } - else if (ft.equalsIgnoreCase("endgroup")) + else if (ft.equalsIgnoreCase(ENDGROUP)) { // We should check whether this is the current group, // but at present there's no way of showing more than 1 group @@ -288,6 +328,43 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI } /** + * Reads input lines from STARTFILTERS to ENDFILTERS and adds a feature type + * filter to the map for each line parsed. After exit from this method, + * nextLine() should return the line after ENDFILTERS (or we are already at + * end of file if ENDFILTERS was missing). + * + * @param filters + * @throws IOException + */ + protected void parseFilters(Map filters) + throws IOException + { + String line; + while ((line = nextLine()) != null) + { + if (line.toUpperCase().startsWith(ENDFILTERS)) + { + return; + } + String[] tokens = line.split(TAB_REGEX); + if (tokens.length != 2) + { + System.err.println(String.format("Invalid token count %d for %d", + tokens.length, line)); + } + else + { + String featureType = tokens[0]; + FeatureMatcherSetI fm = FeatureMatcherSet.fromString(tokens[1]); + if (fm != null && filters != null) + { + filters.put(featureType, fm); + } + } + } + } + + /** * Try to parse a Jalview format feature specification and add it as a * sequence feature to any matching sequences in the alignment. Returns true * if successful (a feature was added), or false if not. @@ -485,15 +562,16 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI } /** - * 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 * source of features * @param visible * map of colour for each visible feature type + * @param featureFilters * @param visibleFeatureGroups * @param includeNonPositional * if true, include non-positional features (regardless of group or @@ -502,6 +580,7 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI */ public String printJalviewFormat(SequenceI[] sequences, Map visible, + Map featureFilters, List visibleFeatureGroups, boolean includeNonPositional) { if (!includeNonPositional && (visible == null || visible.isEmpty())) @@ -529,6 +608,11 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI .toArray(new String[visible.keySet().size()]); /* + * feature filters if any + */ + outputFeatureFilters(out, visible, featureFilters); + + /* * sort groups alphabetically, and ensure that features with a * null or empty group are output after those in named groups */ @@ -558,13 +642,76 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI } } - for (String group : sortedGroups) + /* + * positional features within groups + */ + foundSome |= outputFeaturesByGroup(out, sortedGroups, types, sequences); + + return foundSome ? out.toString() : "No Features Visible"; + } + + /** + * Outputs any feature filters defined for visible feature types, sandwiched by + * STARTFILTERS and ENDFILTERS lines + * + * @param out + * @param visible + * @param featureFilters + */ + void outputFeatureFilters(StringBuilder out, + Map visible, + Map featureFilters) + { + if (visible == null || featureFilters == null + || featureFilters.isEmpty()) + { + return; + } + + boolean first = true; + for (String featureType : visible.keySet()) + { + FeatureMatcherSetI filter = featureFilters.get(featureType); + if (filter != null) + { + if (first) + { + first = false; + out.append(newline).append(STARTFILTERS).append(newline); + } + out.append(featureType).append(TAB).append(filter.toStableString()) + .append(newline); + } + } + if (!first) + { + out.append(ENDFILTERS).append(newline).append(newline); + } + + } + + /** + * Appends output of sequence features within feature groups to the output + * buffer. Groups other than the null or empty group are sandwiched by + * STARTGROUP and ENDGROUP lines. + * + * @param out + * @param groups + * @param featureTypes + * @param sequences + * @return + */ + private boolean outputFeaturesByGroup(StringBuilder out, + List groups, String[] featureTypes, SequenceI[] sequences) + { + boolean foundSome = false; + for (String group : groups) { boolean isNamedGroup = (group != null && !"".equals(group)); if (isNamedGroup) { out.append(newline); - out.append("STARTGROUP").append(TAB); + out.append(STARTGROUP).append(TAB); out.append(group); out.append(newline); } @@ -576,10 +723,10 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI { String sequenceName = sequences[i].getName(); List features = new ArrayList<>(); - if (types.length > 0) + if (featureTypes.length > 0) { features.addAll(sequences[i].getFeatures().getFeaturesForGroup( - true, group, types)); + true, group, featureTypes)); } for (SequenceFeature sequenceFeature : features) @@ -591,13 +738,12 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI if (isNamedGroup) { - out.append("ENDGROUP").append(TAB); + out.append(ENDGROUP).append(TAB); out.append(group); out.append(newline); } } - - return foundSome ? out.toString() : "No Features Visible"; + return foundSome; } /**