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.bio.symbol;
022
023import java.util.ArrayList;
024import java.util.Collections;
025import java.util.Iterator;
026import java.util.List;
027import java.util.ListIterator;
028
029import org.biojava.bio.BioException;
030
031/**
032 * Produced by LocationTools as a result of union operations.
033 * It's a <code>RangeLocation</code> and can be used
034 * as such but it also retains knowledge of which individual components made
035 * it up. None of the methods of RangeLocation are overridden only new methods
036 * have been added to get the subcomponents.</p>
037 *
038 *  <p>For example a union operation between the following locations
039 * [1,20],[27,45],[30-70] will produce a <code>CompoundLocation</code>
040 * like this: {[1,20],[27,70]}, the last two locations have been merged into a
041 * <code>MergeLocation</code> which contains the two subcomponents.</p>
042 *
043 * <p>Copyright: Copyright (c) 2002</p>
044 * <p>Company: AgResearch</p>
045 * @author Mark Schreiber
046 * @author Francois Pepin
047 * @version 1.0
048 */
049
050public class MergeLocation extends RangeLocation {
051  List componentLocations;
052
053  /**
054   * Private constructor, use static method mergeLocations to make an
055   * instance of this class
056   * @param min the minimum point spanned by the location
057   * @param max the maximum point spanned by the location
058   * @param componentLocations the locations that make up this.
059   */
060  private MergeLocation(int min, int max, List componentLocations){
061    super(min, max);
062    this.componentLocations = componentLocations;
063  }
064
065  /**
066   * Gets the component locations that make up this one
067   *
068   * @param recurse if true the method lists the component locations of all
069   * nested <code>MergedLocation</code>s.
070   *
071   * @return a <code>List</code> of <code>Location</code>s.
072   */
073  public List getComponentList(boolean recurse){
074    if(! recurse)
075      return componentLocations;
076
077    else{
078      List l = new ArrayList();
079      for(Iterator i = componentLocations.iterator(); i.hasNext();){
080        Object o = i.next();
081
082        //what if its decorated?
083        if(o instanceof AbstractLocationDecorator){
084          o = ((AbstractLocationDecorator)o).getWrapped();
085        }
086
087        if (o instanceof MergeLocation) {
088          List ll = ((MergeLocation)o).getComponentList(true);
089          l.addAll(ll);
090        }
091        else {
092          l.add(o);
093        }
094
095      }
096
097      return l;
098    }
099  }
100
101  /**
102   * @return A <code>ListIterator</code> over the component locations. The iterator
103   * does not recurse nested MergeLocations.
104   */
105  public ListIterator componentLocationIterator(){
106    return componentLocations.listIterator();
107  }
108
109  /**
110   * Static Factory method for getting an instance of a <code>MergeLocation</code>
111   * @param componentLocations the <code>Locations to Merge</code>
112   * @return the merged location
113   * @throws BioException if the list contains objects that are not <code>Locations</code>
114   *  or if the locations don't represent a contiguous block. Use <code>
115   *  LocationTools.union()</code> if you want to merge discontinuous blocks.
116   */
117  public static MergeLocation mergeLocations(List componentLocations)
118      throws BioException{
119
120    Collections.sort(componentLocations, Location.naturalOrder);
121
122    int lastMax = -1;
123    int lastMin = -1;
124
125    int min = Integer.MAX_VALUE;
126    int max = 0;
127
128    for (Iterator i = componentLocations.iterator(); i.hasNext(); ) {
129      Object item = i.next();
130
131      if(!( item instanceof Location)){
132        throw new BioException(
133            "All members of the component locations list must be Location objects");
134      }
135
136      Location loc = (Location)item;
137
138      //make sure blocks are contiguous
139      if(lastMin != -1 && lastMax != -1){
140        if(!(loc.getMin()+1 <= lastMax)){
141          //blocks aren't contiguous
142          throw new BioException(
143            "All members of the component locations list must be contiguous");
144        }
145      }
146
147      //determine what the min and max should be for the MergeLocation
148      if (loc.getMin() < min) {
149        min = loc.getMin();
150      }
151      if (loc.getMax() > max) {
152        max = loc.getMax();
153      }
154
155      lastMin = loc.getMin(); lastMax = loc.getMax();
156    }
157
158    return new MergeLocation(min, max, componentLocations);
159  }
160
161  public static MergeLocation mergeLocations(Location locA,
162      Location locB) throws BioException{
163
164    int min = Math.min(locA.getMin(), locB.getMin());
165    int max = Math.max(locA.getMax(), locB.getMax());
166
167    List l = new ArrayList(2);
168    l.add(locA);
169    l.add(locB);
170
171    return new MergeLocation(min, max, l);
172  }
173}