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 java.util.ArrayList;
025import java.util.Iterator;
026import java.util.List;
027
028import org.biojava.bio.seq.StrandedFeature;
029import org.biojava.bio.symbol.Location;
030import org.biojava.bio.symbol.LocationTools;
031import org.biojava.bio.symbol.PointLocation;
032import org.biojava.bio.symbol.RangeLocation;
033
034/**
035 * Some common things you want to do while projecting features.
036 *
037 * @author Thomas Down
038 * @author Matthew Pocock
039 * @since 1.3
040 */
041public class ProjectionUtils {
042  /**
043   * Transform a location, translating and flipping as required.
044   *
045   * <p>
046   * If oppositeStrand is false, this is equivalent to translating the location.
047   * If it is true, this is equivalent to flipping it.
048   * </p>
049   *
050   * @param oldLoc          the Location to transform
051   * @param translation     the translation to apply
052   * @param oppositeStrand  wether or not this is a flip
053   * @return  the transformed location
054   */
055  public static Location transformLocation(Location oldLoc,
056                                           int translation,
057                                           boolean oppositeStrand)
058  {
059    if (oppositeStrand) {
060      return flipLocation(oldLoc, translation);
061    } else {
062      return oldLoc.translate(translation);
063    }
064  }
065
066  /**
067   * Revert a location, translating and flipping as required.
068   *
069   * <p>
070   * If oppositeStrand is false, this is equivalent to un-translating the
071   * location. If it is true, this is equivalent to (un)flipping it.
072   * </p>
073   *
074   * @param oldLoc          the Location to revert
075   * @param translation     the translation to unapply
076   * @param oppositeStrand  wether or not this is a flip
077   * @return  the reverted location
078   */
079  public static Location revertLocation(Location oldLoc,
080                                        int translation,
081                                        boolean oppositeStrand)
082  {
083    if (oppositeStrand) {
084      return flipLocation(oldLoc, translation);
085    } else {
086      return oldLoc.translate(-translation);
087    }
088  }
089
090  /**
091   * Flip a location.
092   *
093   * <p>
094   * All points <code>p</code> map to <code>translation - p</code>. Clearly,
095   * this mapping is its own inverse. If you wish to flip all locations between
096   * 1 and length, you should use a translation of length + 1. In general, if
097   * you wish to flip all features between x and y, you should use a translation
098   * of x + y.
099   * </p>
100   *
101   * @param oldLoc      the Location to flip
102   * @param translation the translation to use
103   * @return  the flipped Location
104   */
105  public static Location flipLocation(Location oldLoc, int translation) {
106    if (oldLoc.isContiguous()) {
107      if (oldLoc instanceof PointLocation) {
108        return new PointLocation(translation - oldLoc.getMin());
109      } else {
110        return new RangeLocation(translation - oldLoc.getMax(),
111                                 translation - oldLoc.getMin());
112      }
113    } else {
114      Location compound;
115      List locList = new ArrayList();
116      for (Iterator i = oldLoc.blockIterator(); i.hasNext();) {
117        Location oldBlock = (Location) i.next();
118        locList.add(new RangeLocation(translation - oldBlock.getMax(),
119                                      translation - oldBlock.getMin()));
120      }
121      compound = LocationTools.union(locList);
122      return compound;
123    }
124  }
125
126  public static StrandedFeature.Strand flipStrand(StrandedFeature.Strand s) {
127    if (s == StrandedFeature.POSITIVE) {
128      return StrandedFeature.NEGATIVE;
129    } else if (s == StrandedFeature.NEGATIVE) {
130      return StrandedFeature.POSITIVE;
131    } else {
132      return StrandedFeature.UNKNOWN;
133    }
134  }
135}