View Javadoc

1   //--------------------------------------
2   //
3   // SubSequence.java
4   // Since: 2008/01/25
5   //
6   //--------------------------------------
7   package org.utgenome.gwt.utgb.server.app;
8   
9   import java.awt.Color;
10  import java.io.File;
11  import java.io.IOException;
12  import java.io.PrintWriter;
13  import java.sql.SQLException;
14  import java.util.HashMap;
15  
16  import javax.servlet.ServletException;
17  import javax.servlet.http.HttpServletRequest;
18  import javax.servlet.http.HttpServletResponse;
19  
20  import org.utgenome.UTGBException;
21  import org.utgenome.format.fasta.FASTADatabase;
22  import org.utgenome.format.fasta.FASTADatabase.NSeq;
23  import org.utgenome.graphics.GenomeCanvas;
24  import org.utgenome.graphics.GenomeWindow;
25  import org.utgenome.gwt.utgb.client.bio.ChrLoc;
26  import org.utgenome.gwt.utgb.server.WebTrackBase;
27  import org.utgenome.gwt.utgb.server.util.graphic.GraphicUtil;
28  import org.xerial.db.sql.BeanResultHandler;
29  import org.xerial.json.JSONWriter;
30  import org.xerial.util.log.Logger;
31  import org.xerial.xml.XMLAttribute;
32  import org.xerial.xml.XMLGenerator;
33  
34  /**
35   * SubSequence retrieves a genome sequence of the specified target in the range (start, end) (inclusive)
36   * 
37   * 
38   * @author leo
39   * 
40   */
41  public class Sequence extends WebTrackBase {
42  	private static final long serialVersionUID = 1L;
43  	private static Logger _logger = Logger.getLogger(Sequence.class);
44  
45  	// default values
46  	private static final int DEFAULT_WIDTH = 800;
47  	private static final String DEFAULT_COLOR_A = "50B6E8";
48  	private static final String DEFAULT_COLOR_C = "E7846E";
49  	private static final String DEFAULT_COLOR_G = "84AB51";
50  	private static final String DEFAULT_COLOR_T = "FFA930";
51  	private static final String DEFAULT_COLOR_N = "EEEEEE";
52  	public String colorA = DEFAULT_COLOR_A;
53  	public String colorC = DEFAULT_COLOR_C;
54  	public String colorG = DEFAULT_COLOR_G;
55  	public String colorT = DEFAULT_COLOR_T;
56  	public String colorN = DEFAULT_COLOR_N;
57  
58  	public String dbGroup = "org/utgenome/leo/genome";
59  	public String species = "human";
60  	public String revision = "hg19";
61  	public int start = 0;
62  	public int end = 10000;
63  	public String name = "chr1";
64  	public int width = DEFAULT_WIDTH;
65  	public String path = null;
66  
67  	public Sequence() {
68  	}
69  
70  	@Override
71  	public void validate(HttpServletRequest request, HttpServletResponse response) throws ServletException, UTGBException {
72  		if (width < 0)
73  			width = DEFAULT_WIDTH;
74  
75  		if (start < 0 || end < 0) {
76  			start = 0;
77  			end = 0;
78  		}
79  	}
80  
81  	public static abstract class SequenceRetrieverBase implements BeanResultHandler<NSeq> {
82  
83  		private static Logger _logger = Logger.getLogger(SequenceRetrieverBase.class);
84  
85  		private final int start;
86  		private final int end;
87  		private final boolean isReverseStrand;
88  
89  		private static HashMap<Character, Character> reverseStrandTable = new HashMap<Character, Character>();
90  
91  		static {
92  			reverseStrandTable.put('a', 't');
93  			reverseStrandTable.put('A', 'T');
94  			reverseStrandTable.put('g', 'c');
95  			reverseStrandTable.put('G', 'C');
96  			reverseStrandTable.put('t', 'a');
97  			reverseStrandTable.put('T', 'A');
98  			reverseStrandTable.put('c', 'g');
99  			reverseStrandTable.put('C', 'G');
100 		}
101 
102 		public SequenceRetrieverBase(int start, int end, boolean isReverseStrand) {
103 
104 			assert (start <= end);
105 			this.start = start;
106 			this.end = end;
107 
108 			this.isReverseStrand = isReverseStrand;
109 		}
110 
111 		public void handle(NSeq seq) throws SQLException {
112 			int rangeStart = ((seq.getStart() < start) ? start - seq.getStart() : 0);
113 			int rangeEnd = ((end > seq.getEnd()) ? seq.getLength() : end - seq.getStart() + 1);
114 			output(seq.getSubSequence(rangeStart, rangeEnd));
115 		}
116 
117 		public int getStart() {
118 			return start;
119 		}
120 
121 		public int getEnd() {
122 			return end;
123 		}
124 
125 		public boolean isReverseStrand() {
126 			return isReverseStrand;
127 		}
128 
129 		public abstract void output(String subSequence);
130 
131 		public char getComplement(char base) {
132 			Character ch = reverseStrandTable.get(base);
133 			if (ch == null)
134 				return 'N';
135 			else
136 				return ch.charValue();
137 		}
138 
139 		public void init() {
140 
141 		}
142 
143 		public void finish() {
144 
145 		}
146 
147 		public void handleException(Exception e) throws Exception {
148 			_logger.error(e);
149 		}
150 
151 	}
152 
153 	/**
154 	 * Output a sequence as text
155 	 * 
156 	 * @author leo
157 	 * 
158 	 */
159 	public static class TextOutput extends SequenceRetrieverBase {
160 		private final PrintWriter writer;
161 
162 		public TextOutput(PrintWriter writer, int start, int end, boolean isReverseStrand) {
163 			super(start, end, isReverseStrand);
164 			this.writer = writer;
165 		}
166 
167 		@Override
168 		public void output(String subSequence) {
169 			writer.print(subSequence);
170 		}
171 
172 	}
173 
174 	/**
175 	 * Output a sequence as XML
176 	 * 
177 	 * @author leo
178 	 * 
179 	 */
180 	public static class XMLOutput extends SequenceRetrieverBase {
181 		private final XMLGenerator xml;
182 
183 		public XMLOutput(PrintWriter writer, int start, int end, boolean isReverseStrand) {
184 			super(start, end, isReverseStrand);
185 			xml = new XMLGenerator(writer);
186 		}
187 
188 		@Override
189 		public void init() {
190 			xml.startTag("sequence", new XMLAttribute().add("start", getStart()).add("end", getEnd()));
191 		}
192 
193 		@Override
194 		public void output(String subSequence) {
195 			xml.text(subSequence);
196 		}
197 
198 		@Override
199 		public void finish() {
200 
201 			xml.endDocument();
202 
203 		}
204 
205 	}
206 
207 	/**
208 	 * Output a sequence as JSON
209 	 * 
210 	 * @author leo
211 	 * 
212 	 */
213 	public static class JSONOutput extends SequenceRetrieverBase {
214 		private final JSONWriter jsonWriter;
215 
216 		public JSONOutput(PrintWriter writer, int start, int end, boolean isReverseStrand) {
217 			super(start, end, isReverseStrand);
218 			jsonWriter = new JSONWriter(writer);
219 		}
220 
221 		@Override
222 		public void init() {
223 			try {
224 				jsonWriter.startObject();
225 				jsonWriter.put("start", getStart());
226 				jsonWriter.put("end", getEnd());
227 				jsonWriter.startString("sequence");
228 			}
229 			catch (Exception e) {
230 				_logger.error(e);
231 			}
232 
233 		}
234 
235 		@Override
236 		public void output(String subSequence) {
237 			try {
238 				jsonWriter.append(subSequence);
239 			}
240 			catch (Exception e) {
241 				_logger.error(e);
242 			}
243 		}
244 
245 		@Override
246 		public void finish() {
247 			try {
248 				jsonWriter.endJSON();
249 			}
250 			catch (Exception e) {
251 				_logger.error(e);
252 			}
253 		}
254 
255 	}
256 
257 	public class GraphicalOutput extends SequenceRetrieverBase {
258 		public static final int DEFAULT_HEIGHT = 12;
259 		public static final float FONT_SIZE = 10.5f;
260 		private Color UNKNOWN_BASE_COLOR = GraphicUtil.parseColor(colorN);
261 
262 		protected final GenomeCanvas canvas;
263 		protected int startOffset;
264 		protected int endOffset;
265 		private HashMap<Character, Color> colorTable = new HashMap<Character, Color>();
266 		private final HttpServletResponse response;
267 		private boolean drawBase = false;
268 
269 		public GraphicalOutput(HttpServletResponse response, int start, int end, int width, boolean isReverseStrand) {
270 			super(start, end, isReverseStrand);
271 			this.response = response;
272 			canvas = new GenomeCanvas(width, DEFAULT_HEIGHT, new GenomeWindow(start, end));
273 			this.startOffset = start;
274 			this.endOffset = end + 1;
275 
276 			int seqWidth = end - start;
277 			final int FONT_WIDTH = 7;
278 			if (seqWidth <= width / FONT_WIDTH)
279 				drawBase = true;
280 
281 			int repeatColorAlpha = 50;
282 			colorTable.put('a', GraphicUtil.parseColor(colorA, repeatColorAlpha));
283 			colorTable.put('c', GraphicUtil.parseColor(colorC, repeatColorAlpha));
284 			colorTable.put('g', GraphicUtil.parseColor(colorG, repeatColorAlpha));
285 			colorTable.put('t', GraphicUtil.parseColor(colorT, repeatColorAlpha));
286 			colorTable.put('A', GraphicUtil.parseColor(colorA));
287 			colorTable.put('C', GraphicUtil.parseColor(colorC));
288 			colorTable.put('G', GraphicUtil.parseColor(colorG));
289 			colorTable.put('T', GraphicUtil.parseColor(colorT));
290 		}
291 
292 		public Color getColor(char base) {
293 			Color color = colorTable.get(base);
294 			if (color == null)
295 				return UNKNOWN_BASE_COLOR;
296 			else
297 				return color;
298 		}
299 
300 		@Override
301 		public void output(String subSequence) {
302 			Color textColor = new Color(80, 80, 80);
303 			for (int i = 0; i < subSequence.length(); i++) {
304 				char ch = subSequence.charAt(i);
305 				if (!isReverseStrand()) {
306 					canvas.drawGeneRect(startOffset, startOffset + 1, 0, DEFAULT_HEIGHT, getColor(ch));
307 					if (drawBase)
308 						canvas.drawBase(subSequence.substring(i, i + 1), startOffset, startOffset + 1, DEFAULT_HEIGHT - 2, FONT_SIZE, textColor);
309 
310 					startOffset++;
311 				}
312 				else {
313 					char reverse = getComplement(ch);
314 					canvas.drawGeneRect(endOffset - 1, endOffset, 0, DEFAULT_HEIGHT, getColor(reverse));
315 					if (drawBase)
316 						canvas.drawBase(Character.toString(reverse), endOffset - 1, endOffset, DEFAULT_HEIGHT - 2, FONT_SIZE, textColor);
317 
318 					endOffset--;
319 				}
320 
321 			}
322 		}
323 
324 		@Override
325 		public void finish() {
326 			try {
327 				canvas.outputImage(response, "png");
328 			}
329 			catch (IOException e) {
330 				_logger.error(e);
331 			}
332 		}
333 
334 	}
335 
336 	private class RoughGraphicalOutput extends GraphicalOutput {
337 
338 		private int loopSequenceWidth;
339 
340 		public RoughGraphicalOutput(HttpServletResponse response, int start, int end, int width, boolean isReverseStrand) {
341 			super(response, start, end, width, isReverseStrand);
342 
343 			int range = end - start;
344 			loopSequenceWidth = (int) (range / width + 0.5);
345 
346 		}
347 
348 		@Override
349 		public void output(String subSequence) {
350 			int rangeEnd = startOffset + subSequence.length();
351 			for (int pos = startOffset + startOffset % loopSequenceWidth; pos < rangeEnd; pos += loopSequenceWidth) {
352 
353 				char ch = subSequence.charAt((pos - startOffset));
354 				if (!isReverseStrand()) {
355 					canvas.drawGeneRect(pos, pos + loopSequenceWidth, 0, DEFAULT_HEIGHT, getColor(ch));
356 				}
357 				else {
358 					canvas.drawGeneRect(getEnd() - (pos + loopSequenceWidth), getEnd() - pos, 0, DEFAULT_HEIGHT, getColor(getComplement(ch)));
359 				}
360 			}
361 
362 			startOffset += subSequence.length();
363 		}
364 
365 	}
366 
367 	@Override
368 	public void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
369 
370 		try {
371 
372 			File dbFile = null;
373 			if (path == null) {
374 				String dbFolder = getTrackConfigProperty("utgb.db.folder", getProjectRootPath() + "/db");
375 				dbFile = new File(dbFolder, String.format("%s/%s/%s.sqlite", dbGroup, species, revision));
376 			}
377 			else
378 				dbFile = new File(getProjectRootPath(), path);
379 
380 			ChrLoc queryTarget = new ChrLoc(name, start, end);
381 			int range = queryTarget.length();
382 			boolean isReverseStrand = queryTarget.isAntiSense();
383 
384 			String actionString = getActionSuffix(request);
385 			BeanResultHandler<NSeq> handler = null;
386 			if (actionString.equals("json"))
387 				handler = new JSONOutput(response.getWriter(), start, end, isReverseStrand);
388 			else if (actionString.equals("xml"))
389 				handler = new XMLOutput(response.getWriter(), start, end, isReverseStrand);
390 			else if (actionString.equals("png")) {
391 				if (range > width)
392 					handler = new RoughGraphicalOutput(response, start, end, width, isReverseStrand);
393 				else
394 					handler = new GraphicalOutput(response, start, end, width, isReverseStrand);
395 			}
396 			else
397 				handler = new TextOutput(response.getWriter(), start, end, isReverseStrand);
398 
399 			FASTADatabase.querySequence(dbFile, new ChrLoc(name, start, end), handler);
400 
401 		}
402 		catch (UTGBException e) {
403 			_logger.error(e);
404 			e.printStackTrace();
405 		}
406 	}
407 
408 }