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.gui;
024
025import java.awt.Rectangle;
026import java.beans.PropertyChangeEvent;
027import java.beans.PropertyChangeListener;
028import java.beans.PropertyChangeSupport;
029import java.util.Comparator;
030import java.util.Iterator;
031import java.util.SortedSet;
032import java.util.TreeSet;
033
034import org.biojava.bio.BioError;
035import org.biojava.bio.BioException;
036import org.biojava.bio.BioRuntimeException;
037import org.biojava.bio.dist.Distribution;
038import org.biojava.bio.seq.io.SymbolTokenization;
039import org.biojava.bio.symbol.AtomicSymbol;
040import org.biojava.bio.symbol.FiniteAlphabet;
041import org.biojava.bio.symbol.IllegalSymbolException;
042
043/**
044 * A logo painter that paints in stacked areas.
045 *
046 * @author Matthew Pocock
047 */
048public class StackedLogoPainter implements LogoPainter {
049
050  /**
051   * Supports the bean property logoFont.
052   */
053  private PropertyChangeSupport pcs;
054
055  public void addPropertyChangeListener(PropertyChangeListener listener) {
056    pcs.addPropertyChangeListener(listener);
057  }
058
059  public void removePropertyChangeListener(PropertyChangeListener listener) {
060    pcs.removePropertyChangeListener(listener);
061  }
062
063  public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
064    pcs.addPropertyChangeListener(propertyName, listener);
065  }
066
067  public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
068    pcs.removePropertyChangeListener(propertyName, listener);
069  }
070
071  public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
072    pcs.firePropertyChange(propertyName, oldValue, newValue);
073  }
074
075  public void firePropertyChange(String propertyName, int oldValue, int newValue) {
076    pcs.firePropertyChange(propertyName, oldValue, newValue);
077  }
078
079  public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
080    pcs.firePropertyChange(propertyName, oldValue, newValue);
081  }
082
083  public void firePropertyChange(PropertyChangeEvent evt) {
084    pcs.firePropertyChange(evt);
085  }
086
087  public boolean hasListeners(String propertyName) {
088    return pcs.hasListeners(propertyName);
089  }
090
091  public void paintLogo(LogoContext lCtxt) {
092    Distribution dis = lCtxt.getDistribution();
093    SymbolTokenization toke = null;
094    try {
095        toke = dis.getAlphabet().getTokenization("token");
096    } catch (BioException ex) {
097        throw new BioRuntimeException(ex);
098    }
099
100    Rectangle bounds = lCtxt.getBounds();
101    double height = bounds.getHeight();
102
103    SortedSet info = new TreeSet(new ResValComparator(toke));
104
105    try {
106      for(
107        Iterator i = ((FiniteAlphabet) dis.getAlphabet()).iterator();
108        i.hasNext();
109      ) {
110        AtomicSymbol s = (AtomicSymbol) i.next();
111        info.add(new ResVal(s, dis.getWeight(s) * height));
112      }
113    } catch (IllegalSymbolException ire) {
114      throw new BioError("Symbol dissapeared from dis alphabet", ire);
115    }
116
117    Rectangle r = new Rectangle();
118    r.x = bounds.x;
119    r.y = 0;
120    r.width = bounds.width;
121    for(Iterator i = info.iterator(); i.hasNext();) {
122      ResVal rv = (ResVal) i.next();
123      r.height = (int) rv.getValue();
124
125      lCtxt.getBlockPainter().paintBlock(lCtxt, r, rv.getToken());
126
127      r.y -= rv.getValue();
128    }
129  }
130
131  public StackedLogoPainter() {
132    pcs = new PropertyChangeSupport(this);
133  }
134
135  /**
136   * A symbol/information tuple.
137   */
138  private static class ResVal {
139    private AtomicSymbol symbol;
140    private double value;
141
142    public final AtomicSymbol getToken() {
143      return symbol;
144    }
145
146    public final double getValue() {
147      return value;
148    }
149
150    public ResVal(AtomicSymbol sym, double val) {
151      symbol = sym;
152      value = val;
153    }
154  }
155
156  /**
157   * The comparator for comparing symbol/information tuples.
158   */
159  private static class ResValComparator implements Comparator {
160      private SymbolTokenization toke;
161
162      public ResValComparator(SymbolTokenization toke) {
163          this.toke = toke;
164      }
165
166    public final int compare(Object o1, Object o2) {
167      ResVal rv1 = (ResVal) o1;
168      ResVal rv2 = (ResVal) o2;
169
170      double diff = rv1.getValue() - rv2.getValue();
171      if(diff < 0) return -1;
172      if(diff > 0) return +1;
173      try {
174          return toke.tokenizeSymbol(rv1.getToken()).compareTo(toke.tokenizeSymbol(rv2.getToken()));
175      } catch (IllegalSymbolException ex) {
176          throw new BioError("Couldn't tokenize symbols", ex);
177      }
178    }
179  }
180}