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
021package org.biojava.utils;
022
023/**
024 * This is a ChangeListener that is designed to adapt events of one type from
025 * one source to events of another type emitted by another source. For example,
026 * you could adapt events made by edits in a database to being events fired by
027 * a sequence implementation.
028 *
029 * @author Matthew Pocock
030 * @since 1.1
031 */
032public class ChangeForwarder implements ChangeListener {
033  private final Object source;
034  private final transient ChangeSupport changeSupport;
035
036  /**
037   * Create a new ChangeForwarder for forwarding events.
038   *
039   * @param source        the new source Object
040   * @param changeSupport the ChangeSupport managing the listeners
041   */
042  public ChangeForwarder(Object source, ChangeSupport changeSupport) {
043    this.source = source;
044    this.changeSupport = changeSupport;
045  }
046
047  /**
048   * Retrieve the 'source' object for <code>ChangeEvent</code>s fired by this forwarder.
049   *
050   * @return the source Object
051   */
052  public Object getSource() { return source; }
053
054  /**
055   * Return the underlying <code>ChangeSupport</code> instance that can be used to
056   * fire <code>ChangeEvent</code>s and mannage listeners.
057   *
058   * @return the ChangeSupport delegate
059   */
060  public ChangeSupport changeSupport() { return changeSupport; }
061
062  /**
063   * <p>
064   * Return the new event to represent the originating event ce.
065   * </p>
066   *
067   * <p>
068   * The returned ChangeEvent is the event that will be fired, and should be
069   * built from information in the original event. If it is null, then no event
070   * will be fired.
071   * </p>
072   *
073   * <p>
074   * The default implementation just constructs a ChangeEvent of the same type
075   * that chains back to ce.
076   * </p>
077   *
078   * @param ce  the originating ChangeEvent
079   * @return a new ChangeEvent to pass on, or null if no event should be sent
080   * @throws ChangeVetoException if for any reason this event can't be handled
081   */
082  protected ChangeEvent generateEvent(ChangeEvent ce)
083  throws ChangeVetoException {
084    return new ChangeEvent(
085      getSource(), ce.getType(),
086      null, null,
087      ce
088    );
089  }
090
091  public void preChange(ChangeEvent ce)
092  throws ChangeVetoException {
093    ChangeEvent nce = generateEvent(ce);
094    if(nce != null) {
095      // todo: this should be coupled with the synchronization in postChange
096      synchronized(changeSupport) {
097        changeSupport.firePreChangeEvent(nce);
098      }
099    }
100  }
101
102  public void postChange(ChangeEvent ce) {
103    try {
104      ChangeEvent nce = generateEvent(ce);
105      if(nce != null) {
106        // todo: this should be coupled with the synchronization in preChange
107        synchronized(changeSupport) {
108          changeSupport.firePostChangeEvent(nce);
109        }
110      }
111    } catch (ChangeVetoException cve) {
112      throw new AssertionFailure(
113        "Assertion Failure: Change was vetoed after it had been accepted by preChange",
114        cve
115      );
116    }
117  }
118
119  /**
120   * A ChangeForwarder that systematically uses a given type and wraps the old
121   * event.
122   *
123   * @author Matthew Pocock
124   * @since 1.4
125   */
126  public static class Retyper
127  extends ChangeForwarder{
128    private final ChangeType type;
129
130    /**
131     * Create a new Retyper for forwarding events.
132     *
133     * @param source        the new source Object
134     * @param changeSupport the ChangeSupport managing the listeners
135     * @param type          the new ChangeType
136     */
137    public Retyper(Object source,
138                   ChangeSupport changeSupport,
139                   ChangeType type)
140    {
141      super(source, changeSupport);
142
143      this.type = type;
144    }
145
146    public ChangeType getType() {
147      return type;
148    }
149
150    protected ChangeEvent generateEvent(ChangeEvent ce)
151            throws ChangeVetoException {
152      return new ChangeEvent(getSource(), getType(), null, null, ce);
153    }
154  }
155}