X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fio%2FStockholmFile.java;h=344c4e1e940f9c295bf35b4f8ef6f34a26b179b6;hb=3609d4b908fa64cab35f2348401baab3347188fc;hp=8d340f2fe7eec06b31ed717e19c17688a1b495b0;hpb=d2e495268df6f50286b972f1145f302ad181da00;p=jalview.git diff --git a/src/jalview/io/StockholmFile.java b/src/jalview/io/StockholmFile.java index 8d340f2..344c4e1 100644 --- a/src/jalview/io/StockholmFile.java +++ b/src/jalview/io/StockholmFile.java @@ -28,12 +28,14 @@ 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; @@ -74,23 +76,38 @@ import fr.orsay.lri.varna.models.rna.RNA; */ public class StockholmFile extends AlignFile { + private static final String ANNOTATION = "annotation"; + + private static final char UNDERSCORE = '_'; + private static final Regex OPEN_PAREN = new Regex("(<|\\[)", "("); private static final Regex CLOSE_PAREN = new Regex("(>|\\])", ")"); + // private static final Regex OPEN_PAREN = new Regex("(<|\\[)", "("); + // private static final Regex CLOSE_PAREN = new Regex("(>|\\])", ")"); + public static final Regex DETECT_BRACKETS = new Regex( "(<|>|\\[|\\]|\\(|\\)|\\{|\\})"); + // WUSS extended symbols. Avoid ambiguity with protein SS annotations by using NOT_RNASS first. + public static final String RNASS_BRACKETS = "<>[](){}AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"; + + // 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]*$"); + 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) { @@ -163,8 +180,8 @@ public class StockholmFile extends AlignFile for (int k = 0; k < rna.length(); k++) { - ann[k] = new Annotation(annot[k], "", Rna.getRNASecStrucState( - annot[k]).charAt(0), 0f); + ann[k] = new Annotation(annot[k], "", + Rna.getRNASecStrucState(annot[k]).charAt(0), 0f); } AlignmentAnnotation align = new AlignmentAnnotation("Sec. str.", @@ -207,9 +224,8 @@ public class StockholmFile extends AlignFile r = new Regex("# STOCKHOLM ([\\d\\.]+)"); if (!r.search(nextLine())) { - throw new IOException( - MessageManager - .getString("exception.stockholm_invalid_format")); + throw new IOException(MessageManager + .getString("exception.stockholm_invalid_format")); } else { @@ -218,7 +234,7 @@ public class StockholmFile extends AlignFile // logger.debug("Stockholm version: " + version); } - // We define some Regexes here that will be used regularily later + // We define some Regexes here that will be used regularly later rend = new Regex("^\\s*\\/\\/"); // Find the end of an alignment p = new Regex("(\\S+)\\/(\\d+)\\-(\\d+)"); // split sequence id in // id/from/to @@ -323,17 +339,14 @@ public class StockholmFile extends AlignFile if (accAnnotations != null && accAnnotations.containsKey("AC")) { - if (dbsource != null) + String dbr = (String) accAnnotations.get("AC"); + if (dbr != 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 - // of accession - guessDatabaseFor(seqO, dbr, dbsource); - - } + // we could get very clever here - but for now - just try to + // 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 ? @@ -392,7 +405,7 @@ public class StockholmFile extends AlignFile while (j.hasMoreElements()) { String desc = j.nextElement().toString(); - if ("annotations".equals(desc) && annotsAdded) + if (ANNOTATION.equals(desc) && annotsAdded) { // don't add features if we already added an annotation row continue; @@ -412,7 +425,7 @@ public class StockholmFile extends AlignFile int new_pos = posmap[k]; // look up nearest seqeunce // position to this column SequenceFeature feat = new SequenceFeature(type, desc, - new_pos, new_pos, 0f, null); + new_pos, new_pos, null); seqO.addSequenceFeature(feat); } @@ -439,8 +452,8 @@ public class StockholmFile extends AlignFile { // logger.error("Could not parse sequence line: " + line); throw new IOException(MessageManager.formatMessage( - "exception.couldnt_parse_sequence_line", - new String[] { line })); + "exception.couldnt_parse_sequence_line", new String[] + { line })); } String ns = seqs.get(x.stringMatched(1)); if (ns == null) @@ -518,6 +531,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)); } } @@ -626,16 +642,17 @@ 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"); + String ns = (String) content.get(ANNOTATION); if (ns == null) { @@ -643,7 +660,7 @@ public class StockholmFile extends AlignFile } // finally, append the annotation line ns += seq; - content.put("annotation", ns); + content.put(ANNOTATION, ns); // // end of wrapped annotation block. // // Now a new row is created with the current set of data @@ -671,8 +688,8 @@ public class StockholmFile extends AlignFile // } else { - System.err - .println("Warning - couldn't parse sequence annotation row line:\n" + System.err.println( + "Warning - couldn't parse sequence annotation row line:\n" + line); // throw new IOException("Error parsing " + line); } @@ -680,8 +697,8 @@ public class StockholmFile extends AlignFile else { throw new IOException(MessageManager.formatMessage( - "exception.unknown_annotation_detected", new String[] { - annType, annContent })); + "exception.unknown_annotation_detected", new String[] + { annType, annContent })); } } } @@ -746,6 +763,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"; @@ -794,8 +817,10 @@ public class StockholmFile extends AlignFile { for (DBRefEntry d : dbrs) { - jalview.util.MapList mp = new jalview.util.MapList(new int[] { - seqO.getStart(), seqO.getEnd() }, new int[] { st, en }, 1, 1); + jalview.util.MapList mp = new jalview.util.MapList( + new int[] + { seqO.getStart(), seqO.getEnd() }, new int[] { st, en }, 1, + 1); jalview.datamodel.Mapping mping = new Mapping(mp); d.setMap(mping); } @@ -806,23 +831,29 @@ 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; if (label.contains("_cons")) { - type = (label.indexOf("_cons") == label.length() - 5) ? label - .substring(0, label.length() - 5) : label; + type = (label.indexOf("_cons") == label.length() - 5) + ? label.substring(0, label.length() - 5) + : label; } boolean ss = false, posterior = false; type = id2type(type); + + boolean isrnass = false; + if (type.equalsIgnoreCase("secondary structure")) { ss = true; + isrnass = !NOT_RNASS.search(annots); // sorry about the double negative + // here (it's easier for dealing with + // other non-alpha-non-brace chars) } if (type.equalsIgnoreCase("posterior probability")) { @@ -833,6 +864,10 @@ public class StockholmFile extends AlignFile for (int i = 0; i < annots.length(); i++) { String pos = annots.substring(i, i + 1); + if (UNDERSCORE == pos.charAt(0)) + { + pos = " "; + } Annotation ann; ann = new Annotation(pos, "", ' ', 0f); // 0f is 'valid' null - will not // be written out @@ -840,7 +875,7 @@ public class StockholmFile extends AlignFile { // if (" .-_".indexOf(pos) == -1) { - if (DETECT_BRACKETS.search(pos)) + if (isrnass && RNASS_BRACKETS.indexOf(pos) >= 0) { ann.secondaryStructure = Rna.getRNASecStrucState(pos).charAt(0); ann.displayCharacter = "" + pos.charAt(0); @@ -850,14 +885,14 @@ public class StockholmFile extends AlignFile ann.secondaryStructure = ResidueProperties.getDssp3state(pos) .charAt(0); - if (ann.secondaryStructure == pos.charAt(0)) - { - ann.displayCharacter = ""; // null; // " "; - } - else - { - ann.displayCharacter = " " + ann.displayCharacter; - } + if (ann.secondaryStructure == pos.charAt(0)) + { + ann.displayCharacter = ""; // null; // " "; + } + else + { + ann.displayCharacter = " " + ann.displayCharacter; + } } } @@ -913,15 +948,13 @@ public class StockholmFile extends AlignFile return annot; } - @Override - public String print(SequenceI[] s, boolean jvSuffix) + private String dbref_to_ac_record(DBRefEntry ref) { - String string = print(s, jvSuffix, false); - return string; + return ref.getSource().toString() + " ; " + + ref.getAccessionId().toString(); } - - public String print(SequenceI[] s, boolean jvSuffix, - boolean removeAnnotations) + @Override + public String print(SequenceI[] s, boolean jvSuffix) { out = new StringBuffer(); out.append("# STOCKHOLM 1.0"); @@ -932,13 +965,12 @@ public class StockholmFile extends AlignFile int maxid = 0; int in = 0; Hashtable dataRef = null; + boolean isAA = s[in].isProtein(); while ((in < s.length) && (s[in] != null)) { + String tmp = printId(s[in], jvSuffix); - if (s[in].getSequence().length > max) - { - max = s[in].getSequence().length; - } + max = Math.max(max, s[in].getLength()); if (tmp.length() > maxid) { @@ -946,17 +978,33 @@ public class StockholmFile extends AlignFile } if (s[in].getDBRefs() != null) { - for (int idb = 0; idb < s[in].getDBRefs().length; idb++) + if (dataRef == null) + { + dataRef = new Hashtable(); + } + List primrefs = s[in].getPrimaryDBRefs(); + if (primrefs.size() >= 1) { - if (dataRef == null) + dataRef.put(tmp, dbref_to_ac_record(primrefs.get(0))); + } + else + { + for (int idb = 0; idb < s[in].getDBRefs().length; idb++) { - dataRef = new Hashtable(); + DBRefEntry dbref = s[in].getDBRefs()[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 + .equals(DBRefUtils.getCanonicalName(dbref.getSource()))) + { + break; + } + if (!isAA && DBRefSource.EMBL + .equals(DBRefUtils.getCanonicalName(dbref.getSource()))) + { + break; + } } - - String datAs1 = s[in].getDBRefs()[idb].getSource().toString() - + " ; " - + s[in].getDBRefs()[idb].getAccessionId().toString(); - dataRef.put(tmp, datAs1); } } in++; @@ -987,9 +1035,10 @@ public class StockholmFile extends AlignFile { Object idd = en.nextElement(); String type = (String) dataRef.remove(idd); - out.append(new Format("%-" + (maxid - 2) + "s").form("#=GS " - + idd.toString() + " ")); - if (type.contains("PFAM") || type.contains("RFAM")) + out.append(new Format("%-" + (maxid - 2) + "s") + .form("#=GS " + idd.toString() + " ")); + if (isAA && type.contains("UNIPROT") + || (!isAA && type.contains("EMBL"))) { out.append(" AC " + type.substring(type.indexOf(";") + 1)); @@ -1002,17 +1051,18 @@ public class StockholmFile extends AlignFile } } - - // output annotations - while (i < s.length && s[i] != null) + // output annotations + while (i < s.length && s[i] != null) + { + AlignmentAnnotation[] alAnot = s[i].getAnnotation(); + if (alAnot != null) { - AlignmentAnnotation[] alAnot = s[i].getAnnotation(); - if (alAnot != null && !removeAnnotations) + Annotation[] ann; + + for (int j = 0; j < alAnot.length; j++) { - Annotation[] ann; - for (int j = 0; j < alAnot.length; j++) + if (alAnot[j].annotations != null) { - String key = type2id(alAnot[j].label); boolean isrna = alAnot[j].isValidStruc(); @@ -1042,61 +1092,60 @@ public class StockholmFile extends AlignFile } } - out.append(new Format("%-" + maxid + "s") - .form(printId(s[i], jvSuffix) + " ")); - out.append(s[i].getSequenceAsString()); - out.append(newline); - i++; } - if (!removeAnnotations) + out.append(new Format("%-" + maxid + "s") + .form(printId(s[i], jvSuffix) + " ")); + out.append(s[i].getSequenceAsString()); + out.append(newline); + i++; + } + + // alignment annotation + AlignmentAnnotation aa; + if (al.getAlignmentAnnotation() != null) { - // alignment annotation - AlignmentAnnotation aa; - if (al.getAlignmentAnnotation() != null) + for (int ia = 0; ia < al.getAlignmentAnnotation().length; ia++) { - for (int ia = 0; ia < al.getAlignmentAnnotation().length; ia++) + aa = al.getAlignmentAnnotation()[ia]; + if (aa.autoCalculated || !aa.visible || aa.sequenceRef != null) { - aa = al.getAlignmentAnnotation()[ia]; - if (aa.autoCalculated || !aa.visible || aa.sequenceRef != null) - { - continue; - } - String seq = ""; - String label; - String key = ""; - if (aa.label.equals("seq")) + continue; + } + String seq = ""; + String label; + String key = ""; + if (aa.label.equals("seq")) + { + label = "seq_cons"; + } + else + { + key = type2id(aa.label.toLowerCase()); + if (key == null) { - label = "seq_cons"; + label = aa.label; } else { - key = type2id(aa.label.toLowerCase()); - if (key == null) - { - label = aa.label; - } - else - { - label = key + "_cons"; - } - } - if (label == null) - { - label = aa.label; + label = key + "_cons"; } - label = label.replace(" ", "_"); + } + if (label == null) + { + label = aa.label; + } + label = label.replace(" ", "_"); - out.append(new Format("%-" + maxid + "s") - .form("#=GC " + label + " ")); - boolean isrna = aa.isValidStruc(); - for (int j = 0; j < aa.annotations.length; j++) - { - seq += outputCharacter(key, j, isrna, aa.annotations, null); - } - out.append(seq); - out.append(newline); + out.append( + new Format("%-" + maxid + "s").form("#=GC " + label + " ")); + boolean isrna = aa.isValidStruc(); + for (int j = 0; j < aa.annotations.length; j++) + { + seq += outputCharacter(key, j, isrna, aa.annotations, null); } + out.append(seq); + out.append(newline); } } @@ -1106,6 +1155,7 @@ public class StockholmFile extends AlignFile return out.toString(); } + /** * add an annotation character to the output row * @@ -1121,23 +1171,39 @@ public class StockholmFile extends AlignFile { char seq = ' '; Annotation annot = ann[k]; - String ch = (annot == null) ? ((sequenceI == null) ? "-" : Character - .toString(sequenceI.getCharAt(k))) : annot.displayCharacter; + String ch = (annot == null) + ? ((sequenceI == null) ? "-" + : Character.toString(sequenceI.getCharAt(k))) + : (annot.displayCharacter == null + ? String.valueOf(annot.secondaryStructure) + : annot.displayCharacter); + if (ch == null) + { + ch = " "; + } if (key != null && key.equals("SS")) { + char ssannotchar = ' '; + boolean charset = false; if (annot == null) { // sensible gap character - return ' '; + ssannotchar = ' '; + charset = true; } else { // valid secondary structure AND no alternative label (e.g. ' B') if (annot.secondaryStructure > ' ' && ch.length() < 2) { - return annot.secondaryStructure; + ssannotchar = annot.secondaryStructure; + charset = true; } } + if (charset) + { + return (ssannotchar == ' ' && isrna) ? '.' : ssannotchar; + } } if (ch.length() == 0) @@ -1152,9 +1218,31 @@ public class StockholmFile extends AlignFile { seq = ch.charAt(1); } - return seq; + + return (seq == ' ' && key != null && key.equals("SS") && isrna) ? '.' + : 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(); @@ -1162,6 +1250,7 @@ public class StockholmFile extends AlignFile out.append(newline); print(getSeqsAsArray(), false); + out.append("//"); out.append(newline); return out.toString(); } @@ -1191,15 +1280,15 @@ public class StockholmFile extends AlignFile } } - + protected static String id2type(String id) { if (typeIds.containsKey(id)) { return (String) typeIds.get(id); } - System.err.println("Warning : Unknown Stockholm annotation type code " - + id); + System.err.println( + "Warning : Unknown Stockholm annotation type code " + id); return id; } @@ -1220,27 +1309,8 @@ public class StockholmFile extends AlignFile { return key; } - System.err.println("Warning : Unknown Stockholm annotation type: " - + type); + System.err.println( + "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; - } }