001package org.biojava.bio.seq.projection; 002 003import org.biojava.bio.seq.FeatureFilter; 004import org.biojava.bio.seq.FeatureHolder; 005import org.biojava.bio.seq.FilterUtils; 006import org.biojava.bio.seq.StrandedFeature; 007import org.biojava.bio.symbol.Location; 008 009/** 010 * A ProjectionContext that translates and optionaly flips features. 011 * 012 * <p> 013 * Use this to 'reverse complement' a feature hierachy, or just to shift it 014 * sideways a bit. 015 * </p> 016 * 017 * <p> 018 * If the flipping mode is dissabled, then all translated features are projected 019 * as having locations equivalent to feat.getLocation().translate(translation). 020 * If the flipping mode is enabled, then all features are flipped arround 021 * translation so that translation-i becomes translation+i. 022 * </p> 023 * 024 * @author Matthew Pocock 025 */ 026public class TranslateFlipContext 027extends ReparentContext { 028 // Fixme: we should have a simple constructor that takes a 029 // translation and a StrandedFeature.Strand and does the flip 030 private final int translation; 031 private final boolean oppositeStrand; 032 033 /** 034 * Create a new TranslateFlipContext with explicit translation and flip. 035 * 036 * <p> 037 * Locations will be mapped according to the rules in @link ProjectionUtils. 038 * </p> 039 * 040 * @param parent the parent to graft all projected features onto 041 * @param wrapped the featurs to project 042 * @param translate the translation 043 * @param oppositeStrand wether or not to flip 044 */ 045 public TranslateFlipContext(FeatureHolder parent, 046 FeatureHolder wrapped, 047 int translate, 048 boolean oppositeStrand) 049 { 050 super(parent, wrapped); 051 this.translation = translate; 052 this.oppositeStrand = oppositeStrand; 053 } 054 055 /** 056 * Create a new TranslateFlipContext that flips all featurs arround min and 057 * max. 058 * 059 * <p> 060 * A Location at exactly min will become one at max, and a Location at exactly 061 * max will become one at min. 062 * </p> 063 * 064 * <p> 065 * This is equivalent to 066 * <code>TranslateFlipContext(parent, wrapped, min + max, true)</code> and is 067 * provided to make client code more readable. 068 * </p> 069 * 070 * @param parent the parent to graft all projected features ont 071 * @param wrapped the features to project 072 * @param min the lower position 073 * @param max the higher position 074 */ 075 public TranslateFlipContext(FeatureHolder parent, 076 FeatureHolder wrapped, 077 int min, 078 int max) 079 { 080 super(parent, wrapped); 081 082 if(min > max) { 083 throw new IllegalArgumentException("Max must not be less than min: " + 084 min + "," + max); 085 } 086 087 this.translation = min + max; 088 this.oppositeStrand = true; 089 } 090 091 092 /** 093 * Create a new TranslateFlipContext with translation only. 094 * 095 * <p> 096 * This is equivalent to 097 * <code>TranslateFlipContext(parent, wrapped, translation, false)</code> and 098 * is provided to make client code more readable. 099 * </p> 100 * 101 * @param parent the parent to graft all projected features onto 102 * @param wrapped the featurs to project 103 * @param translation the translation 104 */ 105 public TranslateFlipContext(FeatureHolder parent, 106 FeatureHolder wrapped, 107 int translation) 108 { 109 super(parent, wrapped); 110 111 this.translation = translation; 112 this.oppositeStrand = false; 113 } 114 115 public final int getTranslation() { 116 return translation; 117 } 118 119 public final boolean isOppositeStrand() { 120 return oppositeStrand; 121 } 122 123 public Location projectLocation(Location oldLoc) { 124 return oldLoc.newInstance(ProjectionUtils.transformLocation( 125 oldLoc, translation, oppositeStrand)); 126 } 127 128 public final Location revertLocation(Location oldLoc) { 129 return oldLoc.newInstance(ProjectionUtils.revertLocation( 130 oldLoc, translation, oppositeStrand)); 131 } 132 133 public final StrandedFeature.Strand projectStrand(StrandedFeature.Strand strand) { 134 if (oppositeStrand) { 135 return strand.flip(); 136 } else { 137 return strand; 138 } 139 } 140 141 public final StrandedFeature.Strand revertStrand(StrandedFeature.Strand strand) { 142 return projectStrand(strand); 143 } 144 145 protected FilterUtils.FilterTransformer getTransformer() { 146 final FilterUtils.FilterTransformer delegate = super.getTransformer(); 147 148 return new FilterUtils.FilterTransformer() { 149 public FeatureFilter transform(FeatureFilter ff) { 150 ff = delegate.transform(ff); 151 152 if (ff instanceof FeatureFilter.OverlapsLocation) { 153 return new FeatureFilter.OverlapsLocation(projectLocation(((FeatureFilter.OverlapsLocation) ff).getLocation())); 154 } else if (ff instanceof FeatureFilter.ContainedByLocation) { 155 return new FeatureFilter.ContainedByLocation(projectLocation(((FeatureFilter.ContainedByLocation) ff).getLocation())); 156 } else if (ff instanceof FeatureFilter.StrandFilter) { 157 return new FeatureFilter.StrandFilter(projectStrand(((FeatureFilter.StrandFilter) ff).getStrand())); 158 } else { 159 return ff; 160 } 161 } 162 }; 163 } 164 165 protected FilterUtils.FilterTransformer getReverter() { 166 final FilterUtils.FilterTransformer delegate = super.getReverter(); 167 168 return new FilterUtils.FilterTransformer() { 169 public FeatureFilter transform(FeatureFilter ff) { 170 ff = delegate.transform(ff); 171 172 if (ff instanceof FeatureFilter.OverlapsLocation) { 173 return new FeatureFilter.OverlapsLocation(revertLocation(((FeatureFilter.OverlapsLocation) ff).getLocation())); 174 } else if (ff instanceof FeatureFilter.ContainedByLocation) { 175 return new FeatureFilter.ContainedByLocation(revertLocation(((FeatureFilter.ContainedByLocation) ff).getLocation())); 176 } else if (ff instanceof FeatureFilter.StrandFilter) { 177 return new FeatureFilter.StrandFilter(revertStrand(((FeatureFilter.StrandFilter) ff).getStrand())); 178 } else { 179 return ff; 180 } 181 } 182 }; 183 } 184}