001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021package org.biojava.nbio.structure; 022 023import java.util.ArrayList; 024import java.util.Iterator; 025import java.util.List; 026 027import org.slf4j.Logger; 028import org.slf4j.LoggerFactory; 029 030/** 031 * A chain, a start residue, and an end residue. 032 * 033 * Also stores a length. Because of insertion codes, this length is not necessarily {@code end − start}. 034 */ 035public class ResidueRangeAndLength extends ResidueRange { 036 private static final Logger logger = LoggerFactory.getLogger(ResidueRangeAndLength.class); 037 038 private final int length; 039 040 public ResidueRangeAndLength(String chain, ResidueNumber start, ResidueNumber end, int length) { 041 super(chain, start, end); 042 this.length = length; 043 } 044 045 public ResidueRangeAndLength(String chain, String start, String end, int length) { 046 super(chain, start, end); 047 this.length = length; 048 } 049 050 /** 051 * Returns a new Iterator over every {@link ResidueNumber} in this ResidueRange. 052 * Stores the contents of {@code map} until the iterator is finished, so calling code should set the iterator to {@code null} if it did not finish. 053 */ 054 @Override 055 public Iterator<ResidueNumber> iterator(AtomPositionMap map) { 056 return super.iterator(map); // just a bit faster 057 } 058 059 /** 060 * Calculates the combined number of residues of the ResidueRanges in {@code rrs}. 061 * 062 * Assumes no overlap. If two or more ranges cover the same residues, will over-count the union of the residues. 063 * 064 * @param rrs 065 * A list of ResidueRanges 066 * @return The combined length 067 * @throws IllegalArgumentException 068 * If the {@link #getLength() length} of one or more ResidueRange is null 069 * @see #getLength() 070 */ 071 public static int calcLength(List<ResidueRangeAndLength> rrs) { 072 int l = 0; 073 for (ResidueRangeAndLength rr : rrs) { 074 l += rr.getLength(); 075 } 076 return l; 077 } 078 079 /** 080 * Parses a residue range. 081 * 082 * The AtomPositionMap is used to calculate the length and fill in missing 083 * information, such as for whole chains ('A:'). Supports the special chain 084 * name '_' for single-chain structures. 085 * 086 * If residues are specified outside of the range given in the map, 087 * attempts to decrease the input range to valid values. In extreme cases 088 * where this process fails fails to find any valid indices, returns null. 089 * 090 * For a function which more conservatively represents the input range, 091 * without chain inference and error fixes, use {@link ResidueRange#parse(String)}. 092 * @param s 093 * A string of the form chain_start-end. For example: <code>A.5-100</code>. 094 * @return The unique ResidueRange corresponding to {@code s}. 095 */ 096 public static ResidueRangeAndLength parse(String s, AtomPositionMap map) { 097 ResidueRange rr = parse(s); 098 ResidueNumber start = rr.getStart(); 099 100 String chain = rr.getChainName(); 101 102 // handle special "_" chain 103 if(chain == null || chain.equals("_")) { 104 ResidueNumber first = map.getNavMap().firstKey(); 105 chain = first.getChainName(); 106 // Quick check for additional chains. Not guaranteed if the atoms are out of order. 107 if( ! map.getNavMap().lastKey().getChainName().equals(chain) ) { 108 logger.warn("Multiple possible chains match '_'. Using chain {}",chain); 109 } 110 } 111 112 // get a non-null start and end 113 // if it's the whole chain, choose the first and last residue numbers in the chain 114 if (start==null) { 115 start = map.getFirst(chain); 116 } 117 ResidueNumber end = rr.getEnd(); 118 if (end==null) { // should happen iff start==null 119 end = map.getLast(chain); 120 } 121 122 // Replace '_' 123 start.setChainName(chain); 124 end.setChainName(chain); 125 126 // Now fix any errors and calculate the length 127 return map.trimToValidResidues(new ResidueRange(chain, start, end)); 128 } 129 130 131 public static List<ResidueRangeAndLength> parseMultiple(List<String> ranges, AtomPositionMap map) { 132 List<ResidueRangeAndLength> rrs = new ArrayList<ResidueRangeAndLength>(ranges.size()); 133 for (String range : ranges) { 134 ResidueRangeAndLength rr = ResidueRangeAndLength.parse(range, map); 135 if (rr != null) rrs.add(rr); 136 } 137 return rrs; 138 } 139 140 /** 141 * @param s 142 * A string of the form chain_start-end,chain_start-end, ... For example: 143 * <code>A.5-100,R_110-190,Z_200-250</code>. 144 * @return The unique ResidueRange corresponding to {@code s}. 145 */ 146 public static List<ResidueRangeAndLength> parseMultiple(String s, AtomPositionMap map) { 147 String[] parts = s.split(","); 148 List<ResidueRangeAndLength> list = new ArrayList<ResidueRangeAndLength>(parts.length); 149 for (String part : parts) { 150 list.add(parse(part, map)); 151 } 152 return list; 153 } 154 155 /** 156 * @return The number of residues in this ResidueRange 157 */ 158 public int getLength() { 159 return length; 160 } 161 162 @Override 163 public boolean equals(Object o) { 164 if (this == o) { 165 return true; 166 } 167 if (o == null || getClass() != o.getClass()) { 168 return false; 169 } 170 if (!super.equals(o)) { 171 return false; 172 } 173 ResidueRangeAndLength that = (ResidueRangeAndLength) o; 174 return length == that.length; 175 } 176 177 @Override 178 public int hashCode() { 179 int result = super.hashCode(); 180 result = 31 * result + length; 181 return result; 182 } 183}