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.gui.sequence; 022 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.Iterator; 026import java.util.LinkedList; 027import java.util.List; 028import java.util.Map; 029 030import org.biojava.bio.seq.ByLocationMinMaxFeatureComparator; 031import org.biojava.bio.seq.Feature; 032import org.biojava.bio.seq.FeatureFilter; 033import org.biojava.bio.seq.FeatureHolder; 034import org.biojava.utils.ChangeEvent; 035import org.biojava.utils.ChangeSupport; 036import org.biojava.utils.ChangeType; 037import org.biojava.utils.ChangeVetoException; 038 039 040/** 041 * A SequenceRenderer that renders a set of Features that match a FeatureFilter in such a way that 042 * they do not overlap in the display. 043 * 044 * @author Mark Southern 045 * @since 1.5 046 */ 047public abstract class AbstractPeptideDigestRenderer extends MultiLineRenderer 048{ 049 public static final ChangeType DIGEST = new ChangeType("The peptide digest has changed", 050 "org.biojava.bio.gui.sequence.AbstractPeptideDigestRenderer", "DIGEST", 051 SequenceRenderContext.REPAINT 052 ); 053 public static final String LANE = "Lane"; 054 private FeatureSource source; 055 private FeatureFilter digestFilter; 056 private Map laneMap = new HashMap(); 057 private int laneCount = 0; 058 private int distanceBetween = 0; 059 060 public AbstractPeptideDigestRenderer() 061 { 062 super(); 063 } 064 065 public AbstractPeptideDigestRenderer(FeatureSource source) 066 { 067 this(); 068 setFeatureSource(source); 069 } 070 071 public AbstractPeptideDigestRenderer(FeatureSource source, FeatureFilter filter) 072 { 073 this(source); 074 setFilter(filter); 075 } 076 077 public AbstractPeptideDigestRenderer(FeatureSource source, FeatureFilter filter, int distanceBetweenFeatures) 078 { 079 this(source,filter); 080 setDistanceBetweenFeatures(distanceBetweenFeatures); 081 } 082 083 public void setFeatureSource(FeatureSource source) 084 { 085 this.source = source; 086 } 087 088 public FeatureSource getFeatureSource() 089 { 090 return source; 091 } 092 093 public FeatureFilter getFilter() 094 { 095 return digestFilter; 096 } 097 098 public void setFilter(FeatureFilter filter) 099 { 100 digestFilter = filter; 101 } 102 103 /* 104 * Sets the space between rendered features. Increase for greater visibility. 105 */ 106 public void setDistanceBetweenFeatures(int d) 107 { 108 distanceBetween = d; 109 } 110 111 public int getDistanceBetweenFeatures() 112 { 113 return distanceBetween; 114 } 115 116 public void sortPeptidesIntoLanes() throws ChangeVetoException 117 { 118 if (hasListeners(DIGEST)) 119 { 120 ChangeSupport cs = getChangeSupport(SequenceRenderContext.REPAINT); 121 122 synchronized (cs) 123 { 124 ChangeEvent ce = new ChangeEvent(this, DIGEST); 125 cs.firePreChangeEvent(ce); 126 doSortPeptides(); 127 doRefreshRenderers(); 128 cs.firePostChangeEvent(ce); 129 } 130 } 131 else 132 { 133 doSortPeptides(); 134 doRefreshRenderers(); 135 } 136 } 137 138 protected void doRefreshRenderers() throws ChangeVetoException 139 { 140 super.clearRenderers(); 141 // resort peptide features into new lanes 142 for (int j = 1; j <= laneCount; j++) 143 { 144 //logger.debug("Adding renderers for lane " + j); 145 FeatureFilter ffilt = new FeatureFilter.And(getFilter(), new LaneFeatureFilter(j)); 146 FeatureBlockSequenceRenderer block = new FeatureBlockSequenceRenderer(); 147 block.setFeatureRenderer(createRenderer(j)); 148 PaddingRenderer pad = new PaddingRenderer(); 149 pad.setPadding(1); 150 pad.setRenderer(new FilteringRenderer(block, ffilt, true)); 151 addRenderer(pad); 152 } 153 } 154 155 /* 156 * Method used to return the given FeatureRenderer for a given lane in the display. 157 */ 158 public abstract FeatureRenderer createRenderer(int lane); 159 160 protected void doSortPeptides() 161 { 162 // clear existing stored features 163 laneMap.clear(); 164 165 //logger.debug("Feature Filter = " + getFilter()); 166 FeatureHolder fh = source.getFeatureHolder().filter(getFilter()); 167 List ranges = new LinkedList(); 168 169 for (Iterator i = fh.features(); i.hasNext();) 170 { 171 ranges.add(( Feature ) i.next()); 172 } 173 174 Collections.sort(ranges, new ByLocationMinMaxFeatureComparator()); 175 176 Integer lane_id = new Integer(1); 177 int i = 0; 178 int pos = 0; 179 180 while (ranges.size() > 0) 181 { 182 /*//logger.info("i=" + i + "\tpos=" + pos + "\tlane_id=" + lane_id + "\tsize=" + 183 ranges.size() 184 ); 185 */ 186 Feature f = ( Feature ) ranges.get(i); 187 188 //logger.info("\tloc=" + f.getLocation()); 189 if (f.getLocation().getMin() > pos) 190 { 191 //logger.info("Adding location " + f.getLocation() + " to lane " + lane_id); 192 pos = f.getLocation().getMax() + distanceBetween; // +1 so there are no adjoining peptides on a track 193 194 // we can make distanceBetween 0 if we can differenciate between adjoining Features with the specific SequenceRenderer implementation 195 ranges.remove(i); 196 laneMap.put(f, lane_id); 197 } else 198 { 199 i++; 200 } 201 202 if (i >= ranges.size()) 203 { 204 i = 0; 205 pos = 0; 206 lane_id = new Integer(lane_id.intValue() + 1); 207 //logger.info("RESET:\t" + i + "\t" + pos + "\t" + lane_id + "\t" + ranges.size()); 208 } 209 } 210 laneCount = lane_id.intValue(); 211 } 212 213 private class LaneFeatureFilter implements FeatureFilter 214 { 215 private int lane; 216 217 public LaneFeatureFilter(int lane) 218 { 219 this.lane = lane; 220 } 221 222 public boolean accept(Feature f) 223 { 224 Integer i = ( Integer ) laneMap.get(f); 225 return (i != null) && (i.intValue() == lane); 226 } 227 } 228}