X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fio%2FStockholmFile.java;h=a850ff17a68e5be092d7c355333a7e1ed6869225;hb=304e64fb34b32659be1bbfd39fb4e15b2f79586e;hp=84e629e043e85055e787ef5d33bc6d4d90d81e58;hpb=26b115b0a77d521da92a06572d9b7819c2d0d49a;p=jalview.git diff --git a/src/jalview/io/StockholmFile.java b/src/jalview/io/StockholmFile.java index 84e629e..a850ff1 100644 --- a/src/jalview/io/StockholmFile.java +++ b/src/jalview/io/StockholmFile.java @@ -28,14 +28,17 @@ import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.Annotation; import jalview.datamodel.DBRefEntry; +import jalview.datamodel.DBRefSource; import jalview.datamodel.Mapping; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; import jalview.schemes.ResidueProperties; import jalview.util.Comparison; +import jalview.util.DBRefUtils; import jalview.util.Format; import jalview.util.MessageManager; +import jalview.util.Platform; import java.io.BufferedReader; import java.io.FileReader; @@ -45,6 +48,7 @@ import java.util.Enumeration; import java.util.Hashtable; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Vector; @@ -76,31 +80,116 @@ public class StockholmFile extends AlignFile { private static final String ANNOTATION = "annotation"; -// private static final Regex OPEN_PAREN = new Regex("(<|\\[)", "("); -// -// private static final Regex CLOSE_PAREN = new Regex("(>|\\])", ")"); - - public static final Regex DETECT_BRACKETS = new Regex( - "(<|>|\\[|\\]|\\(|\\)|\\{|\\})"); - + private static final char UNDERSCORE = '_'; + // WUSS extended symbols. Avoid ambiguity with protein SS annotations by using NOT_RNASS first. + public static final String RNASS_BRACKETS = "<>[](){}AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"; + public static final int REGEX_STOCKHOLM = 0; + + public static final int REGEX_BRACKETS = 1; // use the following regex to decide an annotations (whole) line is NOT an RNA // SS (it contains only E,H,e,h and other non-brace/non-alpha chars) - private static final Regex NOT_RNASS = new Regex( - "^[^<>[\\](){}A-DF-Za-df-z]*$"); + public static final int REGEX_NOT_RNASS = 2; + + private static final int REGEX_ANNOTATION = 3; + + private static final int REGEX_PFAM = 4; + + private static final int REGEX_RFAM = 5; + + private static final int REGEX_ALIGN_END = 6; + + private static final int REGEX_SPLIT_ID = 7; + + private static final int REGEX_SUBTYPE = 8; + + private static final int REGEX_ANNOTATION_LINE = 9; + + private static final int REGEX_REMOVE_ID = 10; + + private static final int REGEX_OPEN_PAREN = 11; + + private static final int REGEX_CLOSE_PAREN = 12; + + public static final int REGEX_MAX = 13; + + private static Regex REGEX[] = new Regex[REGEX_MAX]; + + /** + * Centralize all actual Regex instantialization in Platform. + * // JBPNote: Why is this 'centralisation' better ? + * @param id + * @return + */ + private static Regex getRegex(int id) + { + if (REGEX[id] == null) + { + String pat = null, pat2 = null; + switch (id) + { + case REGEX_STOCKHOLM: + pat = "# STOCKHOLM ([\\d\\.]+)"; + break; + case REGEX_BRACKETS: + // for reference; not used + pat = "(<|>|\\[|\\]|\\(|\\)|\\{|\\})"; + break; + case REGEX_NOT_RNASS: + pat = "^[^<>[\\](){}A-DF-Za-df-z]*$"; + break; + case REGEX_ANNOTATION: + pat = "(\\w+)\\s*(.*)"; + break; + case REGEX_PFAM: + pat = "PF[0-9]{5}(.*)"; + break; + case REGEX_RFAM: + pat = "RF[0-9]{5}(.*)"; + break; + case REGEX_ALIGN_END: + pat = "^\\s*\\/\\/"; + break; + case REGEX_SPLIT_ID: + pat = "(\\S+)\\/(\\d+)\\-(\\d+)"; + break; + case REGEX_SUBTYPE: + pat = "(\\S+)\\s+(\\S*)\\s+(.*)"; + break; + case REGEX_ANNOTATION_LINE: + pat = "#=(G[FSRC]?)\\s+(.*)"; + break; + case REGEX_REMOVE_ID: + pat = "(\\S+)\\s+(\\S+)"; + break; + case REGEX_OPEN_PAREN: + pat = "(<|\\[)"; + pat2 = "("; + break; + case REGEX_CLOSE_PAREN: + pat = "(>|\\])"; + pat2 = ")"; + break; + default: + return null; + } + REGEX[id] = Platform.newRegex(pat, pat2); + } + return REGEX[id]; + } StringBuffer out; // output buffer - AlignmentI al; + private AlignmentI al; public StockholmFile() { } /** - * Creates a new StockholmFile object for output. + * Creates a new StockholmFile object for output */ public StockholmFile(AlignmentI al) { @@ -214,7 +303,7 @@ public class StockholmFile extends AlignFile // First, we have to check that this file has STOCKHOLM format, i.e. the // first line must match - r = new Regex("# STOCKHOLM ([\\d\\.]+)"); + r = getRegex(REGEX_STOCKHOLM); if (!r.search(nextLine())) { throw new IOException(MessageManager @@ -228,19 +317,22 @@ public class StockholmFile extends AlignFile } // We define some Regexes here that will be used regularily later - rend = new Regex("^\\s*\\/\\/"); // Find the end of an alignment - p = new Regex("(\\S+)\\/(\\d+)\\-(\\d+)"); // split sequence id in + rend = getRegex(REGEX_ALIGN_END);//"^\\s*\\/\\/"); // Find the end of an alignment + p = getRegex(REGEX_SPLIT_ID);//"(\\S+)\\/(\\d+)\\-(\\d+)"); // split sequence id in // id/from/to - s = new Regex("(\\S+)\\s+(\\S*)\\s+(.*)"); // Parses annotation subtype - r = new Regex("#=(G[FSRC]?)\\s+(.*)"); // Finds any annotation line - x = new Regex("(\\S+)\\s+(\\S+)"); // split id from sequence + s = getRegex(REGEX_SUBTYPE);// "(\\S+)\\s+(\\S*)\\s+(.*)"); // Parses + // annotation subtype + r = getRegex(REGEX_ANNOTATION_LINE);// "#=(G[FSRC]?)\\s+(.*)"); // Finds any + // annotation line + x = getRegex(REGEX_REMOVE_ID);// "(\\S+)\\s+(\\S+)"); // split id from + // sequence // Convert all bracket types to parentheses (necessary for passing to VARNA) - Regex openparen = new Regex("(<|\\[)", "("); - Regex closeparen = new Regex("(>|\\])", ")"); + Regex openparen = getRegex(REGEX_OPEN_PAREN);//"(<|\\[)", "("); + Regex closeparen = getRegex(REGEX_CLOSE_PAREN);//"(>|\\])", ")"); // // Detect if file is RNA by looking for bracket types -// Regex detectbrackets = new Regex("(<|>|\\[|\\]|\\(|\\))"); + // Regex detectbrackets = getRegex("(<|>|\\[|\\]|\\(|\\))"); rend.optimize(); p.optimize(); @@ -262,8 +354,8 @@ public class StockholmFile extends AlignFile this.noSeqs = seqs.size(); String dbsource = null; - Regex pf = new Regex("PF[0-9]{5}(.*)"); // Finds AC for Pfam - Regex rf = new Regex("RF[0-9]{5}(.*)"); // Finds AC for Rfam + Regex pf = getRegex(REGEX_PFAM); // Finds AC for Pfam + Regex rf = getRegex(REGEX_RFAM); // Finds AC for Rfam if (getAlignmentProperty("AC") != null) { String dbType = getAlignmentProperty("AC").toString(); @@ -332,17 +424,14 @@ public class StockholmFile extends AlignFile if (accAnnotations != null && accAnnotations.containsKey("AC")) { - if (dbsource != null) - { String dbr = (String) accAnnotations.get("AC"); if (dbr != null) { // we could get very clever here - but for now - just try to - // guess accession type from source of alignment plus structure + // guess accession type from type of sequence, source of alignment plus + // structure // of accession guessDatabaseFor(seqO, dbr, dbsource); - - } } // else - do what ? add the data anyway and prompt the user to // specify what references these are ? @@ -507,7 +596,7 @@ public class StockholmFile extends AlignFile */ // Let's save the annotations, maybe we'll be able to do something // with them later... - Regex an = new Regex("(\\w+)\\s*(.*)"); + Regex an = getRegex(REGEX_ANNOTATION); if (an.search(annContent)) { if (an.stringMatched(1).equals("NH")) @@ -527,6 +616,9 @@ public class StockholmFile extends AlignFile treeName = an.stringMatched(2); treeString = new StringBuffer(); } + // TODO: JAL-3532 - this is where GF comments and database references are lost + // suggest overriding this method for Stockholm files to catch and properly + // process CC, DR etc into multivalued properties setAlignmentProperty(an.stringMatched(1), an.stringMatched(2)); } } @@ -635,14 +727,15 @@ public class StockholmFile extends AlignFile if (features.containsKey(this.id2type(type))) { // logger.debug("Found content for " + this.id2type(type)); - content = (Hashtable) features.get(this.id2type(type)); + content = (Hashtable) features + .get(this.id2type(type)); } else { // logger.debug("Creating new content holder for " + // this.id2type(type)); content = new Hashtable(); - features.put(this.id2type(type), content); + features.put(id2type(type), content); } String ns = (String) content.get(ANNOTATION); @@ -755,6 +848,12 @@ public class StockholmFile extends AlignFile st = -1; } } + if (dbsource == null) + { + // make up an origin based on whether the sequence looks like it is nucleotide + // or protein + dbsource = (seqO.isProtein()) ? "PFAM" : "RFAM"; + } if (dbsource.equals("PFAM")) { seqdb = "UNIPROT"; @@ -817,10 +916,9 @@ public class StockholmFile extends AlignFile Vector annotation, String label, String annots) { - String convert1, convert2 = null; - - // convert1 = OPEN_PAREN.replaceAll(annots); - // convert2 = CLOSE_PAREN.replaceAll(convert1); + String convert1, convert2 = null; + // String convert1 = OPEN_PAREN.replaceAll(annots); + // String convert2 = CLOSE_PAREN.replaceAll(convert1); // annots = convert2; String type = label; @@ -837,7 +935,8 @@ public class StockholmFile extends AlignFile if (type.equalsIgnoreCase("secondary structure")) { ss = true; - isrnass = !NOT_RNASS.search(annots); // sorry about the double negative + isrnass = !getRegex(REGEX_NOT_RNASS).search(annots); // sorry about the double + // negative // here (it's easier for dealing with // other non-alpha-non-brace chars) } @@ -850,6 +949,11 @@ public class StockholmFile extends AlignFile for (int i = 0; i < annots.length(); i++) { String pos = annots.substring(i, i + 1); + // TODO 2.12 release: verify this Stockholm IO behaviour change in release notes + if (UNDERSCORE == pos.charAt(0)) + { + pos = " "; + } Annotation ann; ann = new Annotation(pos, "", ' ', 0f); // 0f is 'valid' null - will not // be written out @@ -930,6 +1034,11 @@ public class StockholmFile extends AlignFile return annot; } + private String dbref_to_ac_record(DBRefEntry ref) + { + return ref.getSource().toString() + " ; " + + ref.getAccessionId().toString(); + } @Override public String print(SequenceI[] s, boolean jvSuffix) { @@ -944,6 +1053,7 @@ public class StockholmFile extends AlignFile int slen = s.length; SequenceI seq; Hashtable dataRef = null; + boolean isAA = s[in].isProtein(); while ((in < slen) && ((seq = s[in]) != null)) { String tmp = printId(seq, jvSuffix); @@ -961,14 +1071,24 @@ public class StockholmFile extends AlignFile { dataRef = new Hashtable<>(); } - for (int idb = 0; idb < ndb; idb++) + List primrefs = seq.getPrimaryDBRefs(); + if (primrefs.size() >= 1) { - - DBRefEntry ref = seqrefs.get(idb); - String datAs1 = ref.getSource().toString() - + " ; " - + ref.getAccessionId().toString(); - dataRef.put(tmp, datAs1); + dataRef.put(tmp, dbref_to_ac_record(primrefs.get(0))); + } + else + { + for (int idb = 0; idb < ndb; idb++) + { + DBRefEntry dbref = seqrefs.get(idb); + dataRef.put(tmp, dbref_to_ac_record(dbref)); + // if we put in a uniprot or EMBL record then we're done: + if ((isAA ? DBRefSource.UNIPROT : DBRefSource.EMBL) + .equals(DBRefUtils.getCanonicalName(dbref.getSource()))) + { + break; + } + } } } in++; @@ -998,10 +1118,11 @@ public class StockholmFile extends AlignFile while (en.hasMoreElements()) { Object idd = en.nextElement(); - String type = (String) dataRef.remove(idd); + String type = dataRef.remove(idd); out.append(new Format("%-" + (maxid - 2) + "s") .form("#=GS " + idd.toString() + " ")); - if (type.contains("PFAM") || type.contains("RFAM")) + if (isAA && type.contains("UNIPROT") + || (!isAA && type.contains("EMBL"))) { out.append(" AC " + type.substring(type.indexOf(";") + 1)); @@ -1021,35 +1142,37 @@ public class StockholmFile extends AlignFile if (alAnot != null) { Annotation[] ann; - for (int j = 0, nj = alAnot.length; j < nj; j++) + for (int j = 0; j < alAnot.length; j++) { - String key = type2id(alAnot[j].label); - boolean isrna = alAnot[j].isValidStruc(); - - if (isrna) - { - // hardwire to secondary structure if there is RNA secondary - // structure on the annotation - key = "SS"; - } - if (key == null) + if (alAnot[j].annotations != null) { + String key = type2id(alAnot[j].label); + boolean isrna = alAnot[j].isValidStruc(); - continue; - } + if (isrna) + { + // hardwire to secondary structure if there is RNA secondary + // structure on the annotation + key = "SS"; + } + if (key == null) + { + continue; + } - // out.append("#=GR "); - out.append(new Format("%-" + maxid + "s").form( - "#=GR " + printId(seq, jvSuffix) + " " + key + " ")); - ann = alAnot[j].annotations; - String sseq = ""; - for (int k = 0, nk = ann.length; k < nk; k++) - { - sseq += outputCharacter(key, k, isrna, ann, seq); - } - out.append(sseq); - out.append(newline); + // out.append("#=GR "); + out.append(new Format("%-" + maxid + "s").form( + "#=GR " + printId(s[i], jvSuffix) + " " + key + " ")); + ann = alAnot[j].annotations; + String sseq = ""; + for (int k = 0; k < ann.length; k++) + { + sseq += outputCharacter(key, k, isrna, ann, s[i]); + } + out.append(sseq); + out.append(newline); + } } } @@ -1182,6 +1305,26 @@ public class StockholmFile extends AlignFile : seq; } + /** + * make a friendly ID string. + * + * @param dataName + * @return truncated dataName to after last '/' + */ + private String safeName(String dataName) + { + int b = 0; + while ((b = dataName.indexOf("/")) > -1 && b < dataName.length()) + { + dataName = dataName.substring(b + 1).trim(); + + } + int e = (dataName.length() - dataName.indexOf(".")) + 1; + dataName = dataName.substring(1, e).trim(); + return dataName; + } + + public String print() { out = new StringBuffer(); @@ -1220,6 +1363,7 @@ public class StockholmFile extends AlignFile } } + protected static String id2type(String id) { if (typeIds.containsKey(id)) @@ -1252,23 +1396,4 @@ public class StockholmFile extends AlignFile "Warning : Unknown Stockholm annotation type: " + type); return key; } - - /** - * make a friendly ID string. - * - * @param dataName - * @return truncated dataName to after last '/' - */ - private String safeName(String dataName) - { - int b = 0; - while ((b = dataName.indexOf("/")) > -1 && b < dataName.length()) - { - dataName = dataName.substring(b + 1).trim(); - - } - int e = (dataName.length() - dataName.indexOf(".")) + 1; - dataName = dataName.substring(1, e).trim(); - return dataName; - } }