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