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
022package org.biojava.bio.seq.projection;
023
024import org.biojava.bio.BioException;
025import org.biojava.bio.seq.Feature;
026import org.biojava.bio.seq.FeatureFilter;
027import org.biojava.bio.seq.FeatureHolder;
028import org.biojava.bio.seq.Sequence;
029import org.biojava.utils.ChangeListener;
030import org.biojava.utils.ChangeType;
031import org.biojava.utils.ChangeVetoException;
032
033/**
034 * Interface that defines the projection between original features and
035 * projected features.
036 *
037 * <p>
038 * <em>Note:</em> Implementing this interface is not trivial. It is assumed that
039 * if you are implementing this, you are a power user or developer. All method
040 * documentation is aimed at those groups, not users.
041 * </p>
042 *
043 * <h2>Core projection methods</h2>
044 *
045 * <p>
046 * This interface directly specifies a core set of methods that a
047 * ProjectionContext implementation must provide. These are needed to allow
048 * ProjectedFeatureHolder to connect to the context and for projected features
049 * to be implemented on top of the context.
050 * </p>
051 *
052 * <p>
053 * All of these methods are implemented by @link ReparentContext, and it would
054 * be unwise to try to implement them again independently, but it's a free
055 * world and this is, after all, an interface. Some of the methods have
056 * contracts that are a little tricky to implement without some thought.
057 * </p>
058 *
059 * <p>
060 * The methods projectFeature() and revertFeature() should be used as your
061 * exclusive means for converting features between the two views. If you have
062 * some utility method that produces projections, it /must/ be exclusively
063 * invoked from projectFeature(). Likewise, although it is tempting to cast
064 * the projected feature to Projection, and then to call getProjectedFeature(),
065 * this will break the encapsulation of the context. The only method that should
066 * do this cast is revertFeature().
067 *
068 * <h2>Extra projection methods</h2>
069 *
070 * <p>Projection methods are used by the projection engine to map projected
071 * and unprojected feature properties. They are not declared directly in this
072 * interface, but should be supplied by implementations. They will be picked
073 * up by the projection engine using introspection, and connected to projected
074 * feature instances by run-time code generation.
075 * </p>
076 *
077 * <p>So that the projection methods can be found, they should all follow the
078 * same template. If a feature interface has a property <code>foo</code> of
079 * type <code>Bar</code>, then the feature interface will have the two methods:
080 * <code><pre>
081 * public void setFoo(Bar bar)
082 * public Bar getFoo()
083 * </pre></code>
084 * The projection engine will be looking for a pair of methods in the context
085 * named:
086 * <code>
087 * public Bar projectFoo(Bar bar);
088 * public Bar revertFoo(Bar bar);
089 * </pre></code>
090 * If these methods are found, then the projected feature will have get/set
091 * methods that resemble:
092 * <code><pre>
093 * public void setFoo(Bar bar) {
094 *   getViewedFeature().setFoo(getContext().revertFoo(bar));
095 * }
096 *
097 * public Bar getFoo() {
098 *   return getContext().projectFoo(getViewedFeature().getFoo());
099 * }
100 * </pre></code>
101 * If these methods are not found, then the projected feature will have get/set
102 * methods that resembl:
103 * <code><pre>
104 * public void setFoo(Bar bar) {
105 *   getViewedFeature().setFoo(bar);
106 * }
107 *
108 * public Bar getFoo() {
109 *   return getContext().getViewedFeature().getFoo();
110 * }
111 * </pre></code>
112 * </p>
113 *
114 * <p>
115 * Only those methods defined by the interface of the unprojected feature
116 * will be mapped accross. So, if the context provides projectors for the
117 * strand property but the feature is not a stranded feature, it's projection
118 * will not have a strand property.
119 * </p>
120 *
121 * You should probably not be implementing these yourself. Use the standard
122 * factory methods in SequenceTools to create new sequences that are altered
123 * views of other sequences.
124 *
125 * You will probably want to instantiate @link ReparentContext or @link
126 * TranslateFlipContext to achieve the most commonly needed transformations.
127 * If you do have to implement this, extend one of these two classes.
128 *
129 * Consider extending @link ReparentContext or @link TranslateFlipContex. They
130 * do a lot of complex work for you.
131 *
132 * 
133 * When projecting an original location <code>origLoc</code> to your new
134 * location <code>yourLoc</code>, be sure to make sure the decorations match.
135 * The easiest way to do this is return
136 * <code>origLoc.newInstance(yourLoc)</code>. 
137 *
138 * 
139 * Every ProjectionContext implementation must be a public or package-private
140 * class. This is
141 * because ProjectionEngine wires method calls from the projected features back
142 * into the context. These methods are not necessarily defined by the context
143 * interface (if they are project/revert pairs), so the class itself must be
144 * directly accessible. The ProjectionEngine creates accessory classes in the
145 * same package as the context it is using.
146 *
147 * @author Thomas Down
148 * @author Matthew Pocock
149 * @since 1.4
150 */
151
152public interface ProjectionContext {
153  /**
154   * Get the features before projection.
155   *
156   * <p>
157   * If you are projecting all the features in some feature holder, that is what
158   * this method should return.
159   * </p>
160   *
161   * @return the features before projection
162   */
163  public FeatureHolder getUnprojectedFeatures();
164
165  public FeatureHolder getParent(Feature projFeat);
166
167  /**
168   * Get the sequence for a feature.
169   *
170   * <p>
171   * This will be the return value of <code>projFeat.getParent()</code>.
172   * </p>
173   *
174   * @param projFeat the projected Feature
175   * @return the Sequence of the Feature
176   */
177  public Sequence getSequence(Feature projFeat);
178
179  /**
180   * Project a single feature.
181   *
182   * @param feat the Feature to project
183   * @return a Feature representing feat after being transformed by this context
184   */
185  public Feature projectFeature(Feature feat);
186
187  /**
188   * Project all of the features in a FeatureHolder.
189   *
190   * <p>
191   * <em>Warning:</em> The results of calling this method for features that are
192   * not in getUnprojectedFeatures() is not specified by this API, but it is
193   * reasonable to assume that bad things will happen.
194   * </p>
195   *
196   * @param  features  the FeatureHolder containing the features to project
197   * @return a FeatureHolder containing all the features projected
198   */
199  public FeatureHolder projectFeatures(FeatureHolder features);
200
201  /**
202   * Unproject a feature.
203   *
204   * <p>
205   * This is the inverse opperation to @link projectFeature().
206   * </p>
207   *
208   * <p>
209   * <em>Note: </em> The result of calling this method for a feature that is
210   * not projected through this context is not specified by this API, but it
211   * is reasonable to assume that bad things will happen.
212   * </p>
213   *
214   * @param  projFeat  the Feature to un-project
215   * @return the unprojected feature
216   */
217  public Feature revertFeature(Feature projFeat);
218
219  /**
220   * Transform a filter on unprojected features so that it applies to projected
221   * features.
222   *
223   * @param filt  the FeatureFilter to transform
224   * @return the transformed FeatureFilter
225   */
226  public FeatureFilter projectFilter(FeatureFilter filt);
227
228  /**
229   * Transform a filter on projected features so that it applies to unprojected
230   * features.
231   *
232   * @param filt  the FeatureFilter to transform
233   * @return the transformed FeatureFilter
234   */
235  public FeatureFilter revertFilter(FeatureFilter filt);
236
237  /**
238   * Project all features that are children of feature so that they become
239   * children of parent.
240   *
241   * @param feature  the Feature to project all children of
242   * @param parent   the new parent feature holder
243   * @return  a FeatureHolder containing projections of all children of feature
244   *   so that f.getParent() is equal to parent
245   */
246  public FeatureHolder projectChildFeatures(Feature feature, FeatureHolder parent);
247
248  /**
249   * Create a projected feature with properties matching the template.
250   *
251   * <p>
252   * You will probably implement this by delegating to the unprojected feature
253   * holder. It is imperative that the template properties are unprojected first
254   * so that when the newly created feature is projected, the properties match
255   * up.
256   * <p>
257   * Not every projection context has fully reversible semantics. Use your
258   * discression and come up with a reasonable plan that causes least supprise
259   * to the user.
260   * </p>
261   *
262   * @param projTempl the Feature.Template to instantiate
263   * @return a new projected Feature matching the template as closely as possible
264   * @throws BioException  if there was a problem instantiating the template
265   * @throws ChangeVetoException  if the feature creation was vetoed
266   */
267  public Feature createFeature(Feature.Template projTempl)
268      throws BioException, ChangeVetoException;
269
270  /**
271   * Remove the dying child.
272   *
273   * @param dyingChild  a projected feature to remove
274   * @throws BioException if there is an error removing the feature
275   * @throws ChangeVetoException if the removal of the feature was vetoed
276   */
277  public void removeFeature(Feature dyingChild)
278      throws BioException, ChangeVetoException;
279
280  /**
281   * Create a new projected feature.
282   *
283   * <p>See the notes for @link createFeature(Feature.Template) for
284   * implementation advice.
285   * </p>
286   *
287   * @param projParent  the parent for the newly created feature
288   * @param projTempl  the Feature.Template specifying the new feature
289   * @return a new ProjectedFeature that is a child of projParent
290   * @throws BioException if there was a problem creating the feature
291   * @throws ChangeVetoException if the creation of the feature was vetoed
292   */
293  public Feature createFeature(Feature projParent, Feature.Template projTempl)
294      throws BioException, ChangeVetoException;
295
296  /**
297   * Remove the dying child.
298   *
299   * @param projParent  the projected parent Feature
300   * @param dyingChild  the child Feature to remove
301   * @throws ChangeVetoException if the removal of the feature was vetoed
302   */
303  public void removeFeature(Feature projParent, Feature dyingChild)
304      throws ChangeVetoException, BioException; // fixme: should we be throwing BioException?
305
306  /**
307   * Add a ChangeListener to a projected feature.
308   *
309   * @param projFeat the projected Feature to add the listener for
310   * @param cl  the ChangeListener to add
311   * @param ct  the ChangeType to register it for
312   */
313  public void addChangeListener(Feature projFeat, ChangeListener cl, ChangeType ct);
314
315  /**
316   * Remove a ChangeListener from a projected feature.
317   *
318   * @param projFeat  the projected Feature to remove the listener for
319   * @param cl  the ChangeListener to remove
320   * @param ct  the ChangeType it is registered for
321   */
322  public void removeChangeListener(Feature projFeat, ChangeListener cl, ChangeType ct);
323}
324