001/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */ 002/* 003 * BioJava development code 004 * 005 * This code may be freely distributed and modified under the 006 * terms of the GNU Lesser General Public Licence. This should 007 * be distributed with the code. If you do not have a copy, 008 * see: 009 * 010 * http://www.gnu.org/copyleft/lesser.html 011 * 012 * Copyright for this code is held jointly by the individual 013 * authors. These should be listed in @author doc comments. 014 * 015 * For more information on the BioJava project and its aims, 016 * or to join the biojava-l mailing list, visit the home page 017 * at: 018 * 019 * http://www.biojava.org/ 020 * 021 */ 022 023package org.biojava.bio.program.gff; 024 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028 029import org.biojava.bio.seq.StrandedFeature; 030import org.biojava.utils.SmallMap; 031 032/** 033 * A no-frills implementation of a <span class="type">GFFRecord</span>. 034 * 035 * @author Matthew Pocock 036 * @author Greg Cox 037 * @author Aroul Ramadass 038 * @author Len Trigg 039 * @author Richard Holland 040 */ 041public class SimpleGFFRecord implements GFFRecord { 042 /** 043 * The sequence name. 044 */ 045 private String seqName; 046 /** 047 * The source. 048 */ 049 private String source; 050 /** 051 * The feature type. 052 */ 053 private String feature; 054 /** 055 * The start coordinate. 056 */ 057 private int start; 058 /** 059 * The end coordinate. 060 */ 061 private int end; 062 /** 063 * The feature score. 064 */ 065 private double score; 066 /** 067 * The feature strand. 068 */ 069 private StrandedFeature.Strand strand; 070 /** 071 * The feature frame. 072 */ 073 private int frame; 074 /** 075 * The group-name -> <span class="type">List</span> <attribute> 076 * <span class="type">Map</span> 077 */ 078 private Map groupAttributes; 079 /** 080 * The comment. 081 */ 082 private String comment; 083 084 /** 085 * Create a new SimpleGFFRecord from GFFRecord object 086 * 087 * @param rec - A GFFRecord object 088 */ 089 090 public SimpleGFFRecord(GFFRecord rec) { 091 this.seqName = rec.getSeqName(); 092 this.source = rec.getSource(); 093 this.feature = rec.getFeature(); 094 this.start = rec.getStart(); 095 this.end = rec.getEnd(); 096 this.score = rec.getScore(); 097 this.strand = rec.getStrand(); 098 this.frame = rec.getFrame(); 099 this.comment = rec.getComment(); 100 this.groupAttributes = new SmallMap(rec.getGroupAttributes()); 101 } 102 103 public SimpleGFFRecord( 104 String seqName, 105 String source, 106 String feature, 107 int start, 108 int end, 109 double score, 110 StrandedFeature.Strand strand, 111 int frame, 112 String comment, 113 Map groupAttributes 114 ) { 115 this.seqName = seqName; 116 this.source = source; 117 this.feature = feature; 118 this.start = start; 119 this.end = end; 120 this.score = score; 121 this.strand = strand; 122 this.frame = frame; 123 this.comment = comment; 124 this.groupAttributes = new SmallMap(groupAttributes); 125 } 126 127 128 /** 129 * Create a new SimpleGFFRecord with values set to null or zero 130 */ 131 public SimpleGFFRecord() { 132 this.seqName = null; 133 this.source = null; 134 this.feature = null; 135 this.start = 0; 136 this.end = 0; 137 this.score = 0; 138 this.strand = null; 139 this.frame = 0; 140 this.comment = null; 141 this.groupAttributes = null; 142 } 143 144 /** 145 * Set the sequence name to <span class="arg">seqName</span>. 146 * 147 * @param seqName the new name 148 */ 149 public void setSeqName(String seqName) { 150 this.seqName = seqName; 151 } 152 153 public String getSeqName() { 154 return seqName; 155 } 156 157 /** 158 * Set the feature source to <span class="arg">source</source>. 159 * 160 * @param source the new source 161 */ 162 public void setSource(String source) { 163 this.source = source; 164 } 165 166 public String getSource() { 167 return source; 168 } 169 170 /** 171 * Set the feature type to <span class="arg">type</source>. 172 * 173 * @param feature the new feature type 174 */ 175 public void setFeature(String feature) { 176 this.feature = feature; 177 } 178 179 public String getFeature() { 180 return feature; 181 } 182 183 /** 184 * Set the start coordinate to <span class="arg">start</source>. 185 * 186 * @param start the new start coordinate 187 */ 188 public void setStart(int start) { 189 this.start = start; 190 } 191 192 public int getStart() { 193 return start; 194 } 195 196 /** 197 * Set the end coordinate to <span class="arg">end</source>. 198 * 199 * @param end the new end coordinate 200 */ 201 public void setEnd(int end) { 202 this.end = end; 203 } 204 205 public int getEnd() { 206 return end; 207 } 208 209 /** 210 * Set the score to <span class="arg">score</source>. 211 * <p> 212 * The score must be a double, inclusive of <code>0</code>. 213 * If you wish to indicate that there is no score, then use 214 * <span class="type">GFFRecord</span>.<span class="const">NO_SCORE</span>. 215 * 216 * @param score the new score 217 */ 218 public void setScore(double score) { 219 this.score = score; 220 } 221 222 public double getScore() { 223 return score; 224 } 225 226 /** 227 * Set the strand to <span class="arg">strand</source>. 228 * 229 * @param strand the new Strand 230 */ 231 public void setStrand(StrandedFeature.Strand strand) { 232 this.strand = strand; 233 } 234 235 public StrandedFeature.Strand getStrand() { 236 return strand; 237 } 238 239 /** 240 * Set the frame to <span class="arg">frame</source>. 241 * <p> 242 * The score must be one of <code>{0, 1, 2}</code> or 243 * <span class="type">GFFRecord</span>.<span class="const">NO_FRAME</span>. 244 * 245 * @param frame the frame 246 * @throws IllegalArgumentException if score is not valid. 247 */ 248 public void setFrame(int frame) { 249 if (frame != GFFTools.NO_FRAME && 250 (frame < 0 || frame > 2)) 251 { 252 throw new IllegalArgumentException("Illegal frame: " + frame); 253 } 254 this.frame = frame; 255 } 256 257 public int getFrame() { 258 return frame; 259 } 260 261 /** 262 * Replace the group-attribute <span class="type">Map</span> with 263 * <span class="arg">ga</span>. 264 * <p> 265 * To efficiently add a key, call <span class="method">getGroupAttributes()</span> 266 * and modify the <span class="type">Map</span>. 267 * 268 * @param ga the new group-attribute <span class="type">Map</span> 269 */ 270 public void setGroupAttributes(Map ga) { 271 this.groupAttributes = ga; 272 } 273 274 public Map getGroupAttributes() { 275 if (groupAttributes == null) { 276 groupAttributes = new SmallMap(); 277 } 278 return groupAttributes; 279 } 280 281 /** 282 * Set the comment to <span class="arg">comment</source>. 283 * <p> 284 * If you set it to null, then the comment for this line will be ignored. 285 * 286 * @param comment the new comment 287 */ 288 public void setComment(String comment) { 289 this.comment = comment; 290 } 291 292 public String getComment() { 293 return comment; 294 } 295 296 /** 297 * Create a <span class="type">String</span> representation of 298 * <span class="arg">attMap</span>. 299 * 300 * <span class="arg">attMap</span> is assumed to contain 301 * <span class="type">String</span> keys and 302 * <span class="type">List</span> values. 303 * 304 * @param attMap the <span class="type">Map</span> of attributes and value lists 305 * @return a GFF attribute/value <span class="type">String</span> 306 */ 307 public static String stringifyAttributes(Map attMap) { 308 StringBuffer sBuff = new StringBuffer(); 309 Iterator ki = attMap.keySet().iterator(); 310 while (ki.hasNext()) { 311 String key = (String) ki.next(); 312 sBuff.append(key); 313 List values = (List) attMap.get(key); 314 for (Iterator vi = values.iterator(); vi.hasNext();) { 315 String value = (String) vi.next(); 316 if (isText(value)) { 317 sBuff.append(" \"" + value + "\""); 318 } else { 319 sBuff.append(" " + value); 320 } 321 } 322 if (ki.hasNext()) { 323 sBuff.append(" ;"); 324 } 325 } 326 return sBuff.substring(0); 327 } 328 329 /** 330 * Returns true if a string is "textual". The GFF Spec says that 331 * "textual" values must be quoted. This implementation just tests 332 * if the string contains letters or whitespace. 333 * 334 * @param value a <code>String</code> value. 335 * @return true if value is "textual". 336 */ 337 private static boolean isText(String value) { 338 for (int i = 0; i < value.length(); i++) { 339 char c = value.charAt(i); 340 if (Character.isLetter(c) || Character.isWhitespace(c)) { 341 return true; 342 } 343 } 344 return false; 345 } 346} 347