1
2
3
4
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
36
37
38
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
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
155
156
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
176
177
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
209
210
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 }