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 */ 021 022 023package org.biojava.bio.symbol; 024 025import java.io.NotSerializableException; 026import java.io.ObjectStreamException; 027import java.io.Serializable; 028import java.util.Arrays; 029import java.util.Collections; 030import java.util.Iterator; 031import java.util.List; 032import java.util.NoSuchElementException; 033import java.util.Set; 034 035import org.biojava.bio.Annotation; 036import org.biojava.bio.BioError; 037import org.biojava.bio.BioException; 038import org.biojava.bio.seq.io.DoubleTokenization; 039import org.biojava.bio.seq.io.SymbolTokenization; 040import org.biojava.utils.SingletonList; 041import org.biojava.utils.StaticMemberPlaceHolder; 042import org.biojava.utils.Unchangeable; 043import org.biojava.utils.cache.WeakValueHashMap; 044 045/** 046 * <p> 047 * An efficient implementation of an Alphabet over the infinite set of double 048 * values. 049 * </p> 050 * 051 * <p> 052 * This class can be used to represent lists of floating-point numbers as a 053 * SymbolList with the alphabet DoubleAlphabet. These lists can then be 054 * annotated with features, or fed into dynamic-programming algorithms, or 055 * processed as per any other SymbolList object. 056 * </p> 057 * 058 * <p> 059 * Object identity should be used to decide if two DoubleResidue objects are 060 * the same. DoubleAlphabet ensures that all DoubleAlphabet instances are 061 * canonicalized. 062 * </p> 063 * 064 * @author Matthew Pocock 065 * @author Thomas Down 066 */ 067 068public final class DoubleAlphabet 069 extends 070 Unchangeable 071 implements 072 Alphabet, 073 Serializable 074{ 075 076 public static DoubleAlphabet INSTANCE; 077 078 private Object writeReplace() throws ObjectStreamException { 079 try { 080 return new StaticMemberPlaceHolder(DoubleAlphabet.class.getField("INSTANCE")); 081 } catch (NoSuchFieldException nsfe) { 082 throw new NotSerializableException(nsfe.getMessage()); 083 } 084 } 085 086 /** 087 * <p> 088 * Retrieve a SymbolList view of an array of doubles. 089 * </p> 090 * 091 * <p> 092 * The returned object is a view onto the underlying array, and does not copy 093 * it. Changes made to the original array will alter the resulting SymbolList. 094 * </p> 095 * 096 * @param dArray the array of doubles to view 097 * @return a SymbolList over the DoubleAlphabet that represent the values in 098 * dArray 099 */ 100 public static SymbolList fromArray(double [] dArray) { 101 return new DoubleArray(dArray); 102 } 103 104 /** 105 * Retrieve the single DoubleAlphabet instance. 106 * 107 * @return the singleton DoubleAlphabet instance 108 */ 109 public static DoubleAlphabet getInstance() { 110 if(INSTANCE == null) { 111 INSTANCE = new DoubleAlphabet(); 112 //add an alias 113 AlphabetManager.registerAlphabet("Alphabet of all doubles.", INSTANCE); 114 } 115 116 return INSTANCE; 117 } 118 119 private List alphabets = null; 120 private WeakValueHashMap doubleToSym; 121 122 private DoubleAlphabet() { 123 doubleToSym = new WeakValueHashMap(); 124 } 125 126 /** 127 * Retrieve the Symbol for a double. 128 * 129 * @param val the double to view 130 * @return a DoubleSymbol embodying val 131 */ 132 public DoubleSymbol getSymbol(double val) { 133 Double d = new Double(val); 134 DoubleSymbol sym = (DoubleSymbol) doubleToSym.get(d); 135 if (sym== null) { 136 sym = new DoubleSymbol(val); 137 doubleToSym.put(d, sym); 138 } 139 return sym; 140 } 141 142 /** 143 * Retrieve the symbol for a range of doubles. 144 * 145 * @param minVal the minimum value 146 * @param maxVal that maximum value 147 * @return a DoubleRange containing all doubles between min and max value. 148 */ 149 public DoubleRange getSymbol(double minVal, double maxVal) { 150 // fixme: we should probably fly-weight these 151 return new DoubleRange(minVal, maxVal); 152 } 153 154 public static SubDoubleAlphabet getSubAlphabet(double min, double max) { 155 String name = "SUBDOUBLE["+ min +".."+ max +"]"; 156 if(! AlphabetManager.registered(name)){ 157 AlphabetManager.registerAlphabet(name, new SubDoubleAlphabet(min, max)); 158 } 159 return (SubDoubleAlphabet)AlphabetManager.alphabetForName(name); 160 } 161 162 public Annotation getAnnotation() { 163 return Annotation.EMPTY_ANNOTATION; 164 } 165 166 public boolean contains(Symbol s) { 167 if(s instanceof DoubleSymbol) { 168 return true; 169 } else { 170 return false; 171 } 172 } 173 174 public void validate(Symbol s) throws IllegalSymbolException { 175 if(!contains(s)) { 176 throw new IllegalSymbolException( 177 "Only symbols of type DoubleAlphabet.DoubleSymbol are valid for this alphabet.\n" + 178 "(" + s.getClass() + ") " + s.getName() 179 ); 180 } 181 } 182 183 public List getAlphabets() { 184 if(alphabets == null) { 185 alphabets = new SingletonList(this); 186 } 187 return alphabets; 188 } 189 190 public Symbol getGapSymbol() { 191 return AlphabetManager.getGapSymbol(getAlphabets()); 192 } 193 194 public Symbol getAmbiguity(Set syms) throws IllegalSymbolException { 195 for(Iterator i = syms.iterator(); i.hasNext(); ) { 196 Symbol sym = (Symbol) i.next(); 197 validate(sym); 198 } 199 throw new BioError("Operation not implemented"); 200 } 201 202 public Symbol getSymbol(List symList) throws IllegalSymbolException { 203 if(symList.size() != 1) { 204 throw new IllegalSymbolException( 205 "Can't build symbol from list " + symList.size() + " long" 206 ); 207 } 208 209 Symbol s = (Symbol) symList.get(0); 210 validate(s); 211 return s; 212 } 213 214 public String getName() { 215 return "DOUBLE"; 216 } 217 218 public SymbolTokenization getTokenization(String name) { 219 if(!name.equals("name")) { 220 throw new NoSuchElementException( 221 "No parsers supported by DoubleAlphabet called " + name 222 ); 223 } 224 return new DoubleTokenization(); 225 } 226 227 /** 228 * A single double value. 229 * 230 * Get these via <code>DoubleAlphabet.getSymbol(double)</code>. 231 * 232 * @author Matthew Pocock 233 */ 234 public static class DoubleSymbol 235 extends 236 Unchangeable 237 implements 238 AtomicSymbol, 239 Serializable 240 { 241 private final double val; 242 private final Alphabet matches; 243 244 public Annotation getAnnotation() { 245 return Annotation.EMPTY_ANNOTATION; 246 } 247 248 public String getName() { 249 return val + ""; 250 } 251 252 /** 253 * @return the double value associated with this double symbol 254 */ 255 public double doubleValue() { 256 return val; 257 } 258 259 public Alphabet getMatches() { 260 return matches; 261 } 262 263 public List getSymbols() { 264 return new SingletonList(this); 265 } 266 267 public Set getBases() { 268 return Collections.singleton(this); 269 } 270 271 private DoubleSymbol(double val) { 272 this.val = val; 273 this.matches = new SingletonAlphabet(this); 274 } 275 } 276 277 /** 278 * A range of double values. 279 * 280 * Get these via <code>DoubleAlphabet.getSymbol(double, double)</code>. 281 * 282 * @author Matthew Pocock 283 */ 284 public static class DoubleRange 285 extends 286 Unchangeable 287 implements 288 BasisSymbol, 289 Serializable 290 { 291 private final double minVal; 292 private final double maxVal; 293 private final Alphabet matches; 294 295 public Annotation getAnnotation() { 296 return Annotation.EMPTY_ANNOTATION; 297 } 298 299 public String getName() { 300 return "DoubleRange["+ minVal +".."+ maxVal +"]"; 301 } 302 303 public Alphabet getMatches() { 304 return matches; 305 } 306 307 public List getSymbols() { 308 return Arrays.asList(new Symbol[] { this }); 309 } 310 311 public double getMinValue() { 312 return minVal; 313 } 314 315 public double getMaxValue() { 316 return maxVal; 317 } 318 319 protected DoubleRange(double minVal, double maxVal) { 320 this.minVal = minVal; 321 this.maxVal = maxVal; 322 this.matches = DoubleAlphabet.getSubAlphabet(minVal, maxVal); 323 } 324 } 325 326 /** 327 * A light-weight implementation of SymbolList that allows an array to 328 * appear to be a SymbolList. 329 * 330 * @author Matthew Pocock 331 */ 332 private static class DoubleArray 333 extends 334 AbstractSymbolList 335 implements 336 Serializable 337 { 338 private final double [] dArray; 339 340 public Alphabet getAlphabet() { 341 return INSTANCE; 342 } 343 344 public Symbol symbolAt(int i) { 345 return new DoubleSymbol(dArray[i-1]); 346 } 347 348 public int length() { 349 return dArray.length; 350 } 351 352 public DoubleArray(double [] dArray) { 353 this.dArray = dArray; 354 } 355 } 356 357 /** 358 * A class to represent a contiguous range of double symbols. 359 * 360 * @author Matthew Pocock 361 */ 362 public static class SubDoubleAlphabet 363 extends 364 Unchangeable 365 implements 366 Alphabet, Serializable 367 { 368 private final double min; 369 private final double max; 370 private final String name; 371 372 private SubDoubleAlphabet(double min, double max) { 373 this.min = min; 374 this.max = max; 375 this.name = "SUBDOUBLE["+ min +".."+ max +"]"; 376 } 377 378 /** 379 * To prevent duplication of a what should be a 380 * single instance of an existing alphabet. This method 381 * was written as protected so that subclasses even from 382 * other packages will inherit it. It should only be overridden 383 * with care. 384 */ 385 protected Object readResolve() throws ObjectStreamException { 386 try { 387 return AlphabetManager.alphabetForName(this.getName()); 388 } 389 catch (NoSuchElementException nse) { 390 //a custom alphabet has been sent to your VM, register it. 391 AlphabetManager.registerAlphabet(this.getName(), this); 392 return this; 393 } 394 } 395 396 397 public String getName() { 398 return name; 399 } 400 401 public Annotation getAnnotation() { 402 return Annotation.EMPTY_ANNOTATION; 403 } 404 405 public List getAlphabets() { 406 return Arrays.asList(new Alphabet[] { this }); 407 } 408 409 public Symbol getSymbol(List rl) 410 throws IllegalSymbolException { 411 if(rl.size() != 1) { 412 throw new IllegalSymbolException( 413 "SubDoubleAlphabet is one-dimensional: " + this.getName() + 414 " : " + rl ); 415 } 416 417 Symbol s = (Symbol) rl.get(0); 418 419 validate(s); 420 421 return s; 422 } 423 424 public DoubleSymbol getSymbol(double val) throws IllegalSymbolException { 425 if (val < min || val > max) { 426 throw new IllegalSymbolException( 427 "Could not get Symbol for value " + 428 val + " as it is not in the range " + 429 min + " : " + max 430 ); 431 } 432 433 return DoubleAlphabet.getInstance().getSymbol(val); 434} 435 436 437 public Symbol getAmbiguity(Set syms) { 438 throw new BioError("Operation not implemented"); 439 } 440 441 public Symbol getGapSymbol() { 442 return getInstance().getGapSymbol(); 443 } 444 445 public boolean contains(Symbol s) { 446 if(s instanceof DoubleSymbol) { 447 double val = ((DoubleSymbol) s).doubleValue(); 448 if(val >= min && val <= max) { 449 return true; 450 } 451 } 452 453 if(s instanceof DoubleRange) { 454 DoubleRange dr = (DoubleRange) s; 455 if(dr.getMinValue() >= min || dr.getMaxValue() <= max) { 456 return true; 457 } 458 } 459 460 return false; 461 } 462 463 public void validate(Symbol sym) 464 throws IllegalSymbolException { 465 if(!contains(sym)) { 466 throw new IllegalSymbolException( 467 "This alphabet " + this.getName() + 468 " does not contain the symbol " + sym ); 469 } 470 } 471 472 public SymbolTokenization getTokenization(String name) 473 throws BioException { 474 return getInstance().getTokenization(name); 475 } 476 } 477}