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.dist;
024
025import java.io.IOException;
026import java.io.ObjectInputStream;
027import java.io.ObjectOutputStream;
028import java.io.Serializable;
029
030import org.biojava.bio.BioError;
031import org.biojava.bio.symbol.Alphabet;
032import org.biojava.bio.symbol.AlphabetIndex;
033import org.biojava.bio.symbol.AlphabetManager;
034import org.biojava.bio.symbol.AtomicSymbol;
035import org.biojava.bio.symbol.FiniteAlphabet;
036import org.biojava.bio.symbol.IllegalAlphabetException;
037import org.biojava.bio.symbol.IllegalSymbolException;
038import org.biojava.bio.symbol.Symbol;
039import org.biojava.utils.AbstractChangeable;
040import org.biojava.utils.ChangeEvent;
041import org.biojava.utils.ChangeListener;
042import org.biojava.utils.ChangeSupport;
043import org.biojava.utils.ChangeVetoException;
044
045/**
046 * An encapsulation of a count over the Symbols within a FiniteAlphabet using
047 * an AlphabetIndex object.
048 *
049 * @author Matthew Pocock
050 * @author Mark Schreiber (serialization)
051 * @author Thomas Down (more serialization...)
052 * @since 1.1
053 */
054public final class IndexedCount
055  extends
056    AbstractChangeable
057  implements
058    Count, Serializable
059{
060  static final long serialVersionUID = -1764931829553447679L;  
061    
062  //must be transient as indices may vary between VM's
063  private transient AlphabetIndex indexer;
064  private transient double[] counts;
065  private FiniteAlphabet alpha;
066
067  public Alphabet getAlphabet() {
068    return alpha;
069  }
070
071  public double getCount(AtomicSymbol s) throws IllegalSymbolException {
072    return counts[indexer.indexForSymbol(s)];
073  }
074
075  public void setCount(AtomicSymbol s, double c)
076  throws IllegalSymbolException, ChangeVetoException {
077    if(!hasListeners()) {
078      counts[indexer.indexForSymbol(s)] = c;
079    } else {
080      ChangeSupport changeSupport = getChangeSupport(COUNTS);
081      synchronized(changeSupport) {
082        int index = indexer.indexForSymbol(s);
083        ChangeEvent ce = new ChangeEvent(
084          this, COUNTS,
085          new Object[] { s, new Double(counts[index]) },
086          new Object[] { s, new Double(c) }
087        );
088        changeSupport.firePreChangeEvent(ce);
089        counts[index] = c;
090        changeSupport.firePostChangeEvent(ce);
091      }
092    }
093  }
094
095  public void increaseCount(AtomicSymbol s, double c)
096  throws IllegalSymbolException, ChangeVetoException {
097    if(!hasListeners()) {
098      counts[indexer.indexForSymbol(s)] += c;
099    } else {
100      ChangeSupport changeSupport = getChangeSupport(COUNTS);
101      synchronized(changeSupport) {
102        int index = indexer.indexForSymbol(s);
103        double oc = counts[index];
104        double nc = oc + c;
105        ChangeEvent ce = new ChangeEvent(
106          this, COUNTS,
107          new Object[] { s, new Double(oc) },
108          new Object[] { s, new Double(nc) }
109        );
110        changeSupport.firePreChangeEvent(ce);
111        counts[index] = nc;
112        changeSupport.firePostChangeEvent(ce);
113      }
114    }
115  }
116
117  public void setCounts(Count c)
118  throws IllegalAlphabetException, ChangeVetoException {
119    if(c.getAlphabet() != getAlphabet()) {
120      throw new IllegalAlphabetException(
121        "Alphabet must match: " + c.getAlphabet().getName() +
122        " != " + c.getAlphabet().getName()
123      );
124    }
125
126    try {
127      if(!hasListeners()) {
128        for(int i = 0; i < counts.length; i++) {
129          counts[i] = c.getCount((AtomicSymbol) indexer.symbolForIndex(i));
130        }
131      } else {
132        ChangeSupport changeSupport = getChangeSupport(COUNTS);
133        synchronized(changeSupport) {
134          ChangeEvent ce = new ChangeEvent(
135            this, COUNTS
136          );
137          changeSupport.firePreChangeEvent(ce);
138          for(int i = 0; i < counts.length; i++) {
139            counts[i] = c.getCount((AtomicSymbol) indexer.symbolForIndex(i));
140          }
141          changeSupport.firePostChangeEvent(ce);
142        }
143      }
144    } catch (IllegalSymbolException ise) {
145      throw new BioError(
146        "Assertion Failure: Should have no illegal symbols", ise
147      );
148    }
149  }
150
151  public void zeroCounts()
152  throws ChangeVetoException {
153    if(!hasListeners()) {
154      for(int i = 0; i < counts.length; i++) {
155        counts[i] = 0.0;
156      }
157    } else {
158        ChangeSupport changeSupport = getChangeSupport(COUNTS);
159      synchronized(changeSupport) {
160        ChangeEvent ce = new ChangeEvent(
161          this, COUNTS
162        );
163        changeSupport.firePreChangeEvent(ce);
164        for(int i = 0; i < counts.length; i++) {
165          counts[i] = 0.0;
166        }
167        changeSupport.firePostChangeEvent(ce);
168      }
169    }
170  }
171
172  
173  
174  private static class SymbolWeightMemento implements Serializable {
175      static final long serialVersionUID = 5223128163879670657L;
176      
177      public final Symbol symbol;
178      public final double weight;
179      
180      public SymbolWeightMemento(Symbol s, double weight) {
181          this.symbol = s;
182          this.weight = weight;
183      }
184  }
185  
186  private void writeObject(ObjectOutputStream oos)
187      throws IOException
188  {
189      oos.defaultWriteObject();
190      
191      SymbolWeightMemento[] swm = new SymbolWeightMemento[counts.length];
192      for (int w = 0; w < swm.length; ++w) {
193          swm[w] = new SymbolWeightMemento(indexer.symbolForIndex(w), counts[w]);
194      }
195      oos.writeObject(swm);
196  }
197
198  private void readObject(ObjectInputStream stream)
199    throws IOException, ClassNotFoundException
200  {
201    stream.defaultReadObject();
202    indexer = AlphabetManager.getAlphabetIndex(alpha);
203    counts = new double[alpha.size()];
204    
205    SymbolWeightMemento[] swm = (SymbolWeightMemento[]) stream.readObject();
206    for (int m = 0; m < swm.length; ++m) {
207        try {
208            counts[indexer.indexForSymbol(swm[m].symbol)] = swm[m].weight;
209        } catch (IllegalSymbolException ex) {
210            throw new IOException("Symbol in serialized stream can't be found in the alphabet");
211        }
212    }
213  }
214
215  /**
216   * Get a new IdexedCount for an alphabet using the default indexer.
217   *
218   * @param fa  the FiniteAlphabet to count
219   */
220  public IndexedCount(FiniteAlphabet fa) {
221    this(AlphabetManager.getAlphabetIndex(fa));
222  }
223
224  /**
225   * Get a new InexedCount for an alphabet indexer.
226   *
227   * @param indexer  the AlphabetIndex used to map between symbols and indecies
228   */
229  public IndexedCount(AlphabetIndex indexer) {
230    indexer.addChangeListener(ChangeListener.ALWAYS_VETO, AlphabetIndex.INDEX);
231    this.indexer = indexer;
232    this.counts = new double[indexer.getAlphabet().size()];
233    this.alpha = indexer.getAlphabet();
234  }
235}