/* * 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.ws.sifts; import jalview.analysis.AlignSeq; import jalview.analysis.scoremodels.ScoreMatrix; import jalview.analysis.scoremodels.ScoreModels; import jalview.api.DBRefEntryI; import jalview.api.SiftsClientI; import jalview.datamodel.DBRefEntry; import jalview.datamodel.DBRefSource; import jalview.datamodel.SequenceI; import jalview.io.StructureFile; import jalview.schemes.ResidueProperties; import jalview.structure.StructureMapping; import jalview.util.Comparison; import jalview.util.DBRefUtils; import jalview.util.Format; import jalview.xml.binding.sifts.Entry; import jalview.xml.binding.sifts.Entry.Entity; import jalview.xml.binding.sifts.Entry.Entity.Segment; import jalview.xml.binding.sifts.Entry.Entity.Segment.ListMapRegion.MapRegion; import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue; import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue.CrossRefDb; import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue.ResidueDetail; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.net.URL; import java.net.URLConnection; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.zip.GZIPInputStream; import javax.xml.bind.JAXBContext; import javax.xml.bind.Unmarshaller; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamReader; import MCview.Atom; import MCview.PDBChain; public class SiftsClient implements SiftsClientI { /* * for use in mocking out file fetch for tests only * - reset to null after testing! */ private static File mockSiftsFile; private Entry siftsEntry; private StructureFile pdb; private String pdbId; private String structId; private CoordinateSys seqCoordSys = CoordinateSys.UNIPROT; /** * PDB sequence position to sequence coordinate mapping as derived from SIFTS * record for the identified SeqCoordSys Used for lift-over from sequence * derived from PDB (with first extracted PDBRESNUM as 'start' to the sequence * being annotated with PDB data */ private jalview.datamodel.Mapping seqFromPdbMapping; private static final int BUFFER_SIZE = 4096; public static final int UNASSIGNED = Integer.MIN_VALUE; private static final int PDB_RES_POS = 0; private static final int PDB_ATOM_POS = 1; private static final String NOT_OBSERVED = "Not_Observed"; private static final String SIFTS_FTP_BASE_URL = "http://ftp.ebi.ac.uk/pub/databases/msd/sifts/xml/"; private final static String NEWLINE = System.lineSeparator(); private String curSourceDBRef; private HashSet curDBRefAccessionIdsString; private enum CoordinateSys { UNIPROT("UniProt"), PDB("PDBresnum"), PDBe("PDBe"); private String name; private CoordinateSys(String name) { this.name = name; } public String getName() { return name; } }; private enum ResidueDetailType { NAME_SEC_STRUCTURE("nameSecondaryStructure"), CODE_SEC_STRUCTURE("codeSecondaryStructure"), ANNOTATION("Annotation"); private String code; private ResidueDetailType(String code) { this.code = code; } public String getCode() { return code; } }; /** * Fetch SIFTs file for the given PDBfile and construct an instance of * SiftsClient * * @param pdbId * @throws SiftsException */ public SiftsClient(StructureFile pdb) throws SiftsException { this.pdb = pdb; this.pdbId = pdb.getId(); File siftsFile = getSiftsFile(pdbId); siftsEntry = parseSIFTs(siftsFile); } /** * Parse the given SIFTs File and return a JAXB POJO of parsed data * * @param siftFile * - the GZipped SIFTs XML file to parse * @return * @throws Exception * if a problem occurs while parsing the SIFTs XML */ private Entry parseSIFTs(File siftFile) throws SiftsException { try (InputStream in = new FileInputStream(siftFile); GZIPInputStream gzis = new GZIPInputStream(in);) { // System.out.println("File : " + siftFile.getAbsolutePath()); JAXBContext jc = JAXBContext.newInstance("jalview.xml.binding.sifts"); XMLStreamReader streamReader = XMLInputFactory.newInstance() .createXMLStreamReader(gzis); Unmarshaller um = jc.createUnmarshaller(); return (Entry) um.unmarshal(streamReader); } catch (Exception e) { e.printStackTrace(); throw new SiftsException(e.getMessage()); } } /** * Get a SIFTs XML file for a given PDB Id from Cache or download from FTP * repository if not found in cache * * @param pdbId * @return SIFTs XML file * @throws SiftsException */ public static File getSiftsFile(String pdbId) throws SiftsException { /* * return mocked file if it has been set */ if (mockSiftsFile != null) { return mockSiftsFile; } String siftsFileName = SiftsSettings.getSiftDownloadDirectory() + pdbId.toLowerCase() + ".xml.gz"; File siftsFile = new File(siftsFileName); if (siftsFile.exists()) { // The line below is required for unit testing... don't comment it out!!! System.out.println(">>> SIFTS File already downloaded for " + pdbId); if (isFileOlderThanThreshold(siftsFile, SiftsSettings.getCacheThresholdInDays())) { File oldSiftsFile = new File(siftsFileName + "_old"); siftsFile.renameTo(oldSiftsFile); try { siftsFile = downloadSiftsFile(pdbId.toLowerCase()); oldSiftsFile.delete(); return siftsFile; } catch (IOException e) { e.printStackTrace(); oldSiftsFile.renameTo(siftsFile); return new File(siftsFileName); } } else { return siftsFile; } } try { siftsFile = downloadSiftsFile(pdbId.toLowerCase()); } catch (IOException e) { throw new SiftsException(e.getMessage()); } return siftsFile; } /** * This method enables checking if a cached file has exceeded a certain * threshold(in days) * * @param file * the cached file * @param noOfDays * the threshold in days * @return */ public static boolean isFileOlderThanThreshold(File file, int noOfDays) { Path filePath = file.toPath(); BasicFileAttributes attr; int diffInDays = 0; try { attr = Files.readAttributes(filePath, BasicFileAttributes.class); diffInDays = (int) ((new Date().getTime() - attr.lastModifiedTime().toMillis()) / (1000 * 60 * 60 * 24)); // System.out.println("Diff in days : " + diffInDays); } catch (IOException e) { e.printStackTrace(); } return noOfDays <= diffInDays; } /** * Download a SIFTs XML file for a given PDB Id from an FTP repository * * @param pdbId * @return downloaded SIFTs XML file * @throws SiftsException * @throws IOException */ public static File downloadSiftsFile(String pdbId) throws SiftsException, IOException { if (pdbId.contains(".cif")) { pdbId = pdbId.replace(".cif", ""); } String siftFile = pdbId + ".xml.gz"; String siftsFileFTPURL = SIFTS_FTP_BASE_URL + siftFile; String downloadedSiftsFile = SiftsSettings.getSiftDownloadDirectory() + siftFile; File siftsDownloadDir = new File( SiftsSettings.getSiftDownloadDirectory()); if (!siftsDownloadDir.exists()) { siftsDownloadDir.mkdirs(); } // System.out.println(">> Download ftp url : " + siftsFileFTPURL); // long now = System.currentTimeMillis(); URL url = new URL(siftsFileFTPURL); URLConnection conn = url.openConnection(); InputStream inputStream = conn.getInputStream(); FileOutputStream outputStream = new FileOutputStream( downloadedSiftsFile); byte[] buffer = new byte[BUFFER_SIZE]; int bytesRead = -1; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } outputStream.close(); inputStream.close(); // System.out.println(">>> File downloaded : " + downloadedSiftsFile // + " took " + (System.currentTimeMillis() - now) + "ms"); return new File(downloadedSiftsFile); } /** * Delete the SIFTs file for the given PDB Id in the local SIFTs download * directory * * @param pdbId * @return true if the file was deleted or doesn't exist */ public static boolean deleteSiftsFileByPDBId(String pdbId) { File siftsFile = new File(SiftsSettings.getSiftDownloadDirectory() + pdbId.toLowerCase() + ".xml.gz"); if (siftsFile.exists()) { return siftsFile.delete(); } return true; } /** * Get a valid SIFTs DBRef for the given sequence current SIFTs entry * * @param seq * - the target sequence for the operation * @return a valid DBRefEntry that is SIFTs compatible * @throws Exception * if no valid source DBRefEntry was found for the given sequences */ public DBRefEntryI getValidSourceDBRef(SequenceI seq) throws SiftsException { List dbRefs = seq.getPrimaryDBRefs(); if (dbRefs == null || dbRefs.size() < 1) { throw new SiftsException( "Source DBRef could not be determined. DBRefs might not have been retrieved."); } for (DBRefEntry dbRef : dbRefs) { if (dbRef == null || dbRef.getAccessionId() == null || dbRef.getSource() == null) { continue; } String canonicalSource = DBRefUtils .getCanonicalName(dbRef.getSource()); if (isValidDBRefEntry(dbRef) && (canonicalSource.equalsIgnoreCase(DBRefSource.UNIPROT) || canonicalSource.equalsIgnoreCase(DBRefSource.PDB))) { return dbRef; } } throw new SiftsException("Could not get source DB Ref"); } /** * Check that the DBRef Entry is properly populated and is available in this * SiftClient instance * * @param entry * - DBRefEntry to validate * @return true validation is successful otherwise false is returned. */ boolean isValidDBRefEntry(DBRefEntryI entry) { return entry != null && entry.getAccessionId() != null && isFoundInSiftsEntry(entry.getAccessionId()); } @Override public HashSet getAllMappingAccession() { HashSet accessions = new HashSet(); List entities = siftsEntry.getEntity(); for (Entity entity : entities) { List segments = entity.getSegment(); for (Segment segment : segments) { List mapRegions = segment.getListMapRegion() .getMapRegion(); for (MapRegion mapRegion : mapRegions) { accessions .add(mapRegion.getDb().getDbAccessionId().toLowerCase()); } } } return accessions; } @Override public StructureMapping getSiftsStructureMapping(SequenceI seq, String pdbFile, String chain) throws SiftsException { SequenceI aseq = seq; while (seq.getDatasetSequence() != null) { seq = seq.getDatasetSequence(); } structId = (chain == null) ? pdbId : pdbId + "|" + chain; System.out.println("Getting SIFTS mapping for " + structId + ": seq " + seq.getName()); final StringBuilder mappingDetails = new StringBuilder(128); PrintStream ps = new PrintStream(System.out) { @Override public void print(String x) { mappingDetails.append(x); } @Override public void println() { mappingDetails.append(NEWLINE); } }; HashMap mapping = getGreedyMapping(chain, seq, ps); String mappingOutput = mappingDetails.toString(); StructureMapping siftsMapping = new StructureMapping(seq, pdbFile, pdbId, chain, mapping, mappingOutput); StructureMapping siftsMapping = new StructureMapping(aseq, pdbFile, pdbId, chain, mapping, mappingOutput, seqFromPdbMapping); return siftsMapping; } @Override public HashMap getGreedyMapping(String entityId, SequenceI seq, java.io.PrintStream os) throws SiftsException { List omitNonObserved = new ArrayList(); int nonObservedShiftIndex = 0; // System.out.println("Generating mappings for : " + entityId); Entity entity = null; entity = getEntityById(entityId); String originalSeq = AlignSeq.extractGaps( jalview.util.Comparison.GapChars, seq.getSequenceAsString()); HashMap mapping = new HashMap(); DBRefEntryI sourceDBRef; sourceDBRef = getValidSourceDBRef(seq); // TODO ensure sequence start/end is in the same coordinate system and // consistent with the choosen sourceDBRef // set sequence coordinate system - default value is UniProt if (sourceDBRef.getSource().equalsIgnoreCase(DBRefSource.PDB)) { seqCoordSys = CoordinateSys.PDB; } HashSet dbRefAccessionIdsString = new HashSet(); for (DBRefEntry dbref : seq.getDBRefs()) { dbRefAccessionIdsString.add(dbref.getAccessionId().toLowerCase()); } dbRefAccessionIdsString.add(sourceDBRef.getAccessionId().toLowerCase()); curDBRefAccessionIdsString = dbRefAccessionIdsString; curSourceDBRef = sourceDBRef.getAccessionId(); TreeMap resNumMap = new TreeMap(); List segments = entity.getSegment(); SegmentHelperPojo shp = new SegmentHelperPojo(seq, mapping, resNumMap, omitNonObserved, nonObservedShiftIndex); processSegments(segments, shp); try { populateAtomPositions(entityId, mapping); } catch (Exception e) { e.printStackTrace(); } if (seqCoordSys == CoordinateSys.UNIPROT) { padWithGaps(resNumMap, omitNonObserved); } int seqStart = UNASSIGNED; int seqEnd = UNASSIGNED; int pdbStart = UNASSIGNED; int pdbEnd = UNASSIGNED; if (mapping.isEmpty()) { throw new SiftsException("SIFTS mapping failed"); } Integer[] keys = mapping.keySet().toArray(new Integer[0]); Arrays.sort(keys); seqStart = keys[0]; seqEnd = keys[keys.length - 1]; String matchedSeq = originalSeq; if (seqStart != UNASSIGNED) { pdbStart = mapping.get(seqStart)[PDB_RES_POS]; pdbEnd = mapping.get(seqEnd)[PDB_RES_POS]; int orignalSeqStart = seq.getStart(); if (orignalSeqStart >= 1) { int subSeqStart = (seqStart >= orignalSeqStart) ? seqStart - orignalSeqStart : 0; int subSeqEnd = seqEnd - (orignalSeqStart - 1); subSeqEnd = originalSeq.length() < subSeqEnd ? originalSeq.length() : subSeqEnd; matchedSeq = originalSeq.substring(subSeqStart, subSeqEnd); } else { matchedSeq = originalSeq.substring(1, originalSeq.length()); } } StringBuilder targetStrucSeqs = new StringBuilder(); for (String res : resNumMap.values()) { targetStrucSeqs.append(res); } if (os != null) { MappingOutputPojo mop = new MappingOutputPojo(); mop.setSeqStart(seqStart); mop.setSeqEnd(seqEnd); mop.setSeqName(seq.getName()); mop.setSeqResidue(matchedSeq); mop.setStrStart(pdbStart); mop.setStrEnd(pdbEnd); mop.setStrName(structId); mop.setStrResidue(targetStrucSeqs.toString()); mop.setType("pep"); os.print(getMappingOutput(mop).toString()); os.println(); } return mapping; } void processSegments(List segments, SegmentHelperPojo shp) { SequenceI seq = shp.getSeq(); HashMap mapping = shp.getMapping(); TreeMap resNumMap = shp.getResNumMap(); List omitNonObserved = shp.getOmitNonObserved(); int nonObservedShiftIndex = shp.getNonObservedShiftIndex(); for (Segment segment : segments) { // System.out.println("Mapping segments : " + segment.getSegId() + "\\"s // + segStartEnd); List residues = segment.getListResidue().getResidue(); for (Residue residue : residues) { int pdbeIndex = getLeadingIntegerValue(residue.getDbResNum(), UNASSIGNED); int currSeqIndex = UNASSIGNED; List cRefDbs = residue.getCrossRefDb(); CrossRefDb pdbRefDb = null; for (CrossRefDb cRefDb : cRefDbs) { if (cRefDb.getDbSource().equalsIgnoreCase(DBRefSource.PDB)) { pdbRefDb = cRefDb; } if (cRefDb.getDbCoordSys().equalsIgnoreCase(seqCoordSys.getName()) && isAccessionMatched(cRefDb.getDbAccessionId())) { currSeqIndex = getLeadingIntegerValue(cRefDb.getDbResNum(), UNASSIGNED); if (pdbRefDb != null) { break;// exit loop if pdb and uniprot are already found } } } if (seqCoordSys == seqCoordSys.PDB) // FIXME: is seqCoordSys ever PDBe // ??? { // if the sequence has a primary reference to the PDB, then we are // dealing with a sequence extracted directly from the PDB. In that // case, numbering is PDBe - non-observed residues currSeqIndex = pdbeIndex; } if (currSeqIndex == UNASSIGNED) { continue; } if (!isResidueObserved(residue) && seqCoordSys != seqCoordSys.UNIPROT) { // mapping to PDB or PDBe so we need to bookkeep for the non-observed // SEQRES positions omitNonObserved.add(currSeqIndex); ++nonObservedShiftIndex; } // if (currSeqIndex >= seq.getStart() && currSeqIndex <= seqlength) // // true // numbering // is // not // up // to // seq.getEnd() { int resNum = (pdbRefDb == null) ? getLeadingIntegerValue(residue.getDbResNum(), UNASSIGNED) : getLeadingIntegerValue(pdbRefDb.getDbResNum(), UNASSIGNED); if (isResidueObserved(residue) || seqCoordSys == CoordinateSys.UNIPROT) { char resCharCode = ResidueProperties .getSingleCharacterCode(ResidueProperties .getCanonicalAminoAcid(residue.getDbResName())); resNumMap.put(currSeqIndex, String.valueOf(resCharCode)); } mapping.put(currSeqIndex - nonObservedShiftIndex, new int[] { Integer.valueOf(resNum), UNASSIGNED }); } } } } /** * Get the leading integer part of a string that begins with an integer. * * @param input * - the string input to process * @param failValue * - value returned if unsuccessful * @return */ static int getLeadingIntegerValue(String input, int failValue) { if (input == null) { return failValue; } String[] parts = input.split("(?=\\D)(?<=\\d)"); if (parts != null && parts.length > 0 && parts[0].matches("[0-9]+")) { return Integer.valueOf(parts[0]); } return failValue; } /** * * @param chainId * Target chain to populate mapping of its atom positions. * @param mapping * Two dimension array of residue index versus atom position * @throws IllegalArgumentException * Thrown if chainId or mapping is null * @throws SiftsException */ void populateAtomPositions(String chainId, Map mapping) throws IllegalArgumentException, SiftsException { try { PDBChain chain = pdb.findChain(chainId); if (chain == null || mapping == null) { throw new IllegalArgumentException( "Chain id or mapping must not be null."); } for (int[] map : mapping.values()) { if (map[PDB_RES_POS] != UNASSIGNED) { map[PDB_ATOM_POS] = getAtomIndex(map[PDB_RES_POS], chain.atoms); } } } catch (NullPointerException e) { throw new SiftsException(e.getMessage()); } catch (Exception e) { throw new SiftsException(e.getMessage()); } } /** * * @param residueIndex * The residue index used for the search * @param atoms * A collection of Atom to search * @return atom position for the given residue index */ int getAtomIndex(int residueIndex, Collection atoms) { if (atoms == null) { throw new IllegalArgumentException( "atoms collection must not be null!"); } for (Atom atom : atoms) { if (atom.resNumber == residueIndex) { return atom.atomIndex; } } return UNASSIGNED; } /** * Checks if the residue instance is marked 'Not_observed' or not * * @param residue * @return */ private boolean isResidueObserved(Residue residue) { Set annotations = getResidueAnnotaitons(residue, ResidueDetailType.ANNOTATION); if (annotations == null || annotations.isEmpty()) { return true; } for (String annotation : annotations) { if (annotation.equalsIgnoreCase(NOT_OBSERVED)) { return false; } } return true; } /** * Get annotation String for a given residue and annotation type * * @param residue * @param type * @return */ private Set getResidueAnnotaitons(Residue residue, ResidueDetailType type) { HashSet foundAnnotations = new HashSet(); List resDetails = residue.getResidueDetail(); for (ResidueDetail resDetail : resDetails) { if (resDetail.getProperty().equalsIgnoreCase(type.getCode())) { foundAnnotations.add(resDetail.getContent()); } } return foundAnnotations; } @Override public boolean isAccessionMatched(String accession) { boolean isStrictMatch = true; return isStrictMatch ? curSourceDBRef.equalsIgnoreCase(accession) : curDBRefAccessionIdsString.contains(accession.toLowerCase()); } private boolean isFoundInSiftsEntry(String accessionId) { Set siftsDBRefs = getAllMappingAccession(); return accessionId != null && siftsDBRefs.contains(accessionId.toLowerCase()); } /** * Pad omitted residue positions in PDB sequence with gaps * * @param resNumMap */ void padWithGaps(Map resNumMap, List omitNonObserved) { if (resNumMap == null || resNumMap.isEmpty()) { return; } Integer[] keys = resNumMap.keySet().toArray(new Integer[0]); // Arrays.sort(keys); int firstIndex = keys[0]; int lastIndex = keys[keys.length - 1]; // System.out.println("Min value " + firstIndex); // System.out.println("Max value " + lastIndex); for (int x = firstIndex; x <= lastIndex; x++) { if (!resNumMap.containsKey(x) && !omitNonObserved.contains(x)) { resNumMap.put(x, "-"); } } } @Override public Entity getEntityById(String id) throws SiftsException { // Determines an entity to process by performing a heuristic matching of all // Entities with the given chainId and choosing the best matching Entity Entity entity = getEntityByMostOptimalMatchedId(id); if (entity != null) { return entity; } throw new SiftsException("Entity " + id + " not found"); } /** * This method was added because EntityId is NOT always equal to ChainId. * Hence, it provides the logic to greedily detect the "true" Entity for a * given chainId where discrepancies exist. * * @param chainId * @return */ public Entity getEntityByMostOptimalMatchedId(String chainId) { // System.out.println("---> advanced greedy entityId matching block // entered.."); List entities = siftsEntry.getEntity(); SiftsEntitySortPojo[] sPojo = new SiftsEntitySortPojo[entities.size()]; int count = 0; for (Entity entity : entities) { sPojo[count] = new SiftsEntitySortPojo(); sPojo[count].entityId = entity.getEntityId(); List segments = entity.getSegment(); for (Segment segment : segments) { List residues = segment.getListResidue().getResidue(); for (Residue residue : residues) { List cRefDbs = residue.getCrossRefDb(); for (CrossRefDb cRefDb : cRefDbs) { if (!cRefDb.getDbSource().equalsIgnoreCase("PDB")) { continue; } ++sPojo[count].resCount; if (cRefDb.getDbChainId().equalsIgnoreCase(chainId)) { ++sPojo[count].chainIdFreq; } } } } sPojo[count].pid = (100 * sPojo[count].chainIdFreq) / sPojo[count].resCount; ++count; } Arrays.sort(sPojo, Collections.reverseOrder()); // System.out.println("highest matched entity : " + sPojo[0].entityId); // System.out.println("highest matched pid : " + sPojo[0].pid); if (sPojo[0].entityId != null) { if (sPojo[0].pid < 1) { return null; } for (Entity entity : entities) { if (!entity.getEntityId().equalsIgnoreCase(sPojo[0].entityId)) { continue; } return entity; } } return null; } private class SiftsEntitySortPojo implements Comparable { public String entityId; public int chainIdFreq; public int pid; public int resCount; @Override public int compareTo(SiftsEntitySortPojo o) { return this.pid - o.pid; } } private class SegmentHelperPojo { private SequenceI seq; private HashMap mapping; private TreeMap resNumMap; private List omitNonObserved; private int nonObservedShiftIndex; public SegmentHelperPojo(SequenceI seq, HashMap mapping, TreeMap resNumMap, List omitNonObserved, int nonObservedShiftIndex) { setSeq(seq); setMapping(mapping); setResNumMap(resNumMap); setOmitNonObserved(omitNonObserved); setNonObservedShiftIndex(nonObservedShiftIndex); } public SequenceI getSeq() { return seq; } public void setSeq(SequenceI seq) { this.seq = seq; } public HashMap getMapping() { return mapping; } public void setMapping(HashMap mapping) { this.mapping = mapping; } public TreeMap getResNumMap() { return resNumMap; } public void setResNumMap(TreeMap resNumMap) { this.resNumMap = resNumMap; } public List getOmitNonObserved() { return omitNonObserved; } public void setOmitNonObserved(List omitNonObserved) { this.omitNonObserved = omitNonObserved; } public int getNonObservedShiftIndex() { return nonObservedShiftIndex; } public void setNonObservedShiftIndex(int nonObservedShiftIndex) { this.nonObservedShiftIndex = nonObservedShiftIndex; } } @Override public StringBuilder getMappingOutput(MappingOutputPojo mp) throws SiftsException { String seqRes = mp.getSeqResidue(); String seqName = mp.getSeqName(); int sStart = mp.getSeqStart(); int sEnd = mp.getSeqEnd(); String strRes = mp.getStrResidue(); String strName = mp.getStrName(); int pdbStart = mp.getStrStart(); int pdbEnd = mp.getStrEnd(); String type = mp.getType(); int maxid = (seqName.length() >= strName.length()) ? seqName.length() : strName.length(); int len = 72 - maxid - 1; int nochunks = ((seqRes.length()) / len) + ((seqRes.length()) % len > 0 ? 1 : 0); // output mappings StringBuilder output = new StringBuilder(512); output.append(NEWLINE); output.append("Sequence \u27f7 Structure mapping details") .append(NEWLINE); output.append("Method: SIFTS"); output.append(NEWLINE).append(NEWLINE); output.append(new Format("%" + maxid + "s").form(seqName)); output.append(" : "); output.append(String.valueOf(sStart)); output.append(" - "); output.append(String.valueOf(sEnd)); output.append(" Maps to "); output.append(NEWLINE); output.append(new Format("%" + maxid + "s").form(structId)); output.append(" : "); output.append(String.valueOf(pdbStart)); output.append(" - "); output.append(String.valueOf(pdbEnd)); output.append(NEWLINE).append(NEWLINE); ScoreMatrix pam250 = ScoreModels.getInstance().getPam250(); int matchedSeqCount = 0; for (int j = 0; j < nochunks; j++) { // Print the first aligned sequence output.append(new Format("%" + (maxid) + "s").form(seqName)) .append(" "); for (int i = 0; i < len; i++) { if ((i + (j * len)) < seqRes.length()) { output.append(seqRes.charAt(i + (j * len))); } } output.append(NEWLINE); output.append(new Format("%" + (maxid) + "s").form(" ")).append(" "); /* * Print out the match symbols: * | for exact match (ignoring case) * . if PAM250 score is positive * else a space */ for (int i = 0; i < len; i++) { try { if ((i + (j * len)) < seqRes.length()) { char c1 = seqRes.charAt(i + (j * len)); char c2 = strRes.charAt(i + (j * len)); boolean sameChar = Comparison.isSameResidue(c1, c2, false); if (sameChar && !Comparison.isGap(c1)) { matchedSeqCount++; output.append("|"); } else if (type.equals("pep")) { if (pam250.getPairwiseScore(c1, c2) > 0) { output.append("."); } else { output.append(" "); } } else { output.append(" "); } } } catch (IndexOutOfBoundsException e) { continue; } } // Now print the second aligned sequence output = output.append(NEWLINE); output = output.append(new Format("%" + (maxid) + "s").form(strName)) .append(" "); for (int i = 0; i < len; i++) { if ((i + (j * len)) < strRes.length()) { output.append(strRes.charAt(i + (j * len))); } } output.append(NEWLINE).append(NEWLINE); } float pid = (float) matchedSeqCount / seqRes.length() * 100; if (pid < SiftsSettings.getFailSafePIDThreshold()) { throw new SiftsException(">>> Low PID detected for SIFTs mapping..."); } output.append("Length of alignment = " + seqRes.length()) .append(NEWLINE); output.append(new Format("Percentage ID = %2.2f").form(pid)); return output; } @Override public int getEntityCount() { return siftsEntry.getEntity().size(); } @Override public String getDbAccessionId() { return siftsEntry.getDbAccessionId(); } @Override public String getDbCoordSys() { return siftsEntry.getDbCoordSys(); } @Override public String getDbSource() { return siftsEntry.getDbSource(); } @Override public String getDbVersion() { return siftsEntry.getDbVersion(); } public static void setMockSiftsFile(File file) { mockSiftsFile = file; } }