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}