/* * 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.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.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 { private Entry siftsEntry; private StructureFile pdb; private String pdbId; private String structId; private CoordinateSys seqCoordSys = CoordinateSys.UNIPROT; private static final int BUFFER_SIZE = 4096; public static final int UNASSIGNED = -1; 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 { 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); } } } 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); 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); 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; } if (isValidDBRefEntry(dbRef) && dbRef.isPrimary() && (DBRefUtils.getCanonicalName(dbRef.getSource()) .equalsIgnoreCase(DBRefSource.UNIPROT) || DBRefUtils .getCanonicalName(dbRef.getSource()) .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 { structId = (chain == null) ? pdbId : pdbId + "|" + chain; System.out.println("Getting mapping for: " + pdbId + "|" + chain + " : 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); 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; Integer[] keys = mapping.keySet().toArray(new Integer[0]); Arrays.sort(keys); if (keys.length < 1) { throw new SiftsException(">>> Empty SIFTS mapping generated!!"); } 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(pdbStart); mop.setSeqEnd(pdbEnd); mop.setSeqName(seq.getName()); mop.setSeqResidue(matchedSeq); mop.setStrStart(seqStart); mop.setStrEnd(seqEnd); 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 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())) { String resNumIndexString = cRefDb.getDbResNum() .equalsIgnoreCase("None") ? String.valueOf(UNASSIGNED) : cRefDb.getDbResNum(); try { currSeqIndex = Integer.valueOf(resNumIndexString); } catch (NumberFormatException nfe) { currSeqIndex = Integer.valueOf(resNumIndexString .split("[a-zA-Z]")[0]); continue; } if (pdbRefDb != null) { break;// exit loop if pdb and uniprot are already found } } } if (currSeqIndex == UNASSIGNED) { continue; } if (currSeqIndex >= seq.getStart() && currSeqIndex <= seq.getEnd()) { int resNum; try { resNum = (pdbRefDb == null) ? Integer.valueOf(residue .getDbResNum()) : Integer.valueOf(pdbRefDb .getDbResNum()); } catch (NumberFormatException nfe) { resNum = (pdbRefDb == null) ? Integer.valueOf(residue .getDbResNum()) : Integer.valueOf(pdbRefDb .getDbResNum().split("[a-zA-Z]")[0]); continue; } if (isResidueObserved(residue) || seqCoordSys == CoordinateSys.UNIPROT) { char resCharCode = ResidueProperties .getSingleCharacterCode(ResidueProperties .getCanonicalAminoAcid(residue.getDbResName())); resNumMap.put(currSeqIndex, String.valueOf(resCharCode)); } else { omitNonObserved.add(currSeqIndex); ++nonObservedShiftIndex; } mapping.put(currSeqIndex - nonObservedShiftIndex, new int[] { Integer.valueOf(resNum), UNASSIGNED }); } } } } /** * * @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) { 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 StringBuffer 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 StringBuffer output = new StringBuffer(); 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); 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 matching chars for (int i = 0; i < len; i++) { try { if ((i + (j * len)) < seqRes.length()) { if (seqRes.charAt(i + (j * len)) == strRes .charAt(i + (j * len)) && !jalview.util.Comparison.isGap(seqRes.charAt(i + (j * len)))) { matchedSeqCount++; output.append("|"); } else if (type.equals("pep")) { if (ResidueProperties.getPAM250(seqRes.charAt(i + (j * len)), strRes.charAt(i + (j * len))) > 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(); } }