import java.net.URL;
import java.util.Arrays;
import java.util.List;
++import java.util.function.Function;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
/**
-- * A client for the Ensembl lookup REST endpoint; used to find the Parent gene
-- * identifier given a transcript identifier.
++ * A client for the Ensembl lookup REST endpoint
*
* @author gmcarstairs
*/
public class EnsemblLookup extends EnsemblRestClient
{
+ private static final String SPECIES = "species";
- private static final String OBJECT_TYPE_TRANSLATION = "Translation";
private static final String PARENT = "Parent";
+
++ private static final String OBJECT_TYPE_TRANSLATION = "Translation";
+ private static final String OBJECT_TYPE_TRANSCRIPT = "Transcript";
+ private static final String ID = "id";
+ private static final String OBJECT_TYPE_GENE = "Gene";
+ private static final String OBJECT_TYPE = "object_type";
+
/**
* Default constructor (to use rest.ensembl.org)
*/
* @param identifier
* @return
*/
- public String getParent(String identifier)
+ public String getGeneId(String identifier)
{
- return getAttribute(identifier, PARENT);
++ return getResult(identifier, br -> parseGeneId(br));
+ }
+
+ /**
+ * Calls the Ensembl lookup REST endpoint and retrieves the 'species' for the
+ * given identifier, or null if not found
+ *
+ * @param identifier
+ * @return
+ */
+ public String getSpecies(String identifier)
+ {
- return getAttribute(identifier, SPECIES);
++ return getResult(identifier, br -> getAttribute(br, SPECIES));
+ }
+
+ /**
+ * @param identifier
+ * @param attribute
+ * @return
+ */
- protected String getAttribute(String identifier, String attribute)
++ protected String getResult(String identifier,
++ Function<BufferedReader, String> parser)
+ {
List<String> ids = Arrays.asList(new String[] { identifier });
BufferedReader br = null;
{
br = getHttpResponse(url, ids);
}
- return (parseResponse(br, attribute));
- return br == null ? null : parseResponse(br);
++ return br == null ? null : parser.apply(br);
} catch (IOException e)
{
// ignore
}
/**
- * Parses the value of 'attribute' from the JSON response and returns the
- * value, or null if not found
++ * Answers the value of 'attribute' from the JSON response, or null if not
++ * found
+ *
+ * @param br
+ * @param attribute
+ * @return
- * @throws IOException
+ */
- protected String parseResponse(BufferedReader br, String attribute) throws IOException
++ protected String getAttribute(BufferedReader br, String attribute)
++ {
++ String value = null;
++ JSONParser jp = new JSONParser();
++ try
++ {
++ JSONObject val = (JSONObject) jp.parse(br);
++ value = val.get(attribute).toString();
++ } catch (ParseException | NullPointerException | IOException e)
++ {
++ // ignore
++ }
++ return value;
++ }
++
++ /**
+ * Parses the JSON response and returns the gene identifier, or null if not
+ * found. If the returned object_type is Gene, returns the id, if Transcript
+ * returns the Parent. If it is Translation (peptide identifier), then the
+ * Parent is the transcript identifier, so we redo the search with this value.
+ *
+ * @param br
+ * @return
- * @throws IOException
+ */
- protected String parseResponse(BufferedReader br) throws IOException
++ protected String parseGeneId(BufferedReader br)
{
- String parent = null;
+ String geneId = null;
JSONParser jp = new JSONParser();
try
{
JSONObject val = (JSONObject) jp.parse(br);
- parent = val.get(attribute).toString();
- } catch (ParseException | NullPointerException e)
+ String type = val.get(OBJECT_TYPE).toString();
+ if (OBJECT_TYPE_GENE.equalsIgnoreCase(type))
+ {
+ geneId = val.get(ID).toString();
+ }
+ else if (OBJECT_TYPE_TRANSCRIPT.equalsIgnoreCase(type))
+ {
+ geneId = val.get(PARENT).toString();
+ }
+ else if (OBJECT_TYPE_TRANSLATION.equalsIgnoreCase(type))
+ {
+ String transcriptId = val.get(PARENT).toString();
+ try
+ {
+ geneId = getGeneId(transcriptId);
+ } catch (StackOverflowError e)
+ {
+ /*
+ * unlikely data condition error!
+ */
+ System.err
+ .println("** Ensembl lookup "
+ + getUrl(transcriptId).toString()
+ + " looping on Parent!");
+ }
+ }
- } catch (ParseException e)
++ } catch (ParseException | IOException e)
{
// ignore
}
--- /dev/null
+ package jalview.gui;
+
+ import static org.testng.Assert.assertEquals;
+
+ import jalview.datamodel.AlignmentI;
+ import jalview.io.DataSourceType;
+ import jalview.io.FileLoader;
+
+ import java.awt.Font;
+ import java.awt.FontMetrics;
+
+ import junit.extensions.PA;
+
+ import org.testng.annotations.Test;
+
-import sun.swing.SwingUtilities2;
-
+ public class SeqCanvasTest
+ {
+ /**
+ * Test the method that computes wrapped width in residues, height of wrapped
+ * widths in pixels, and the number of widths visible
+ */
+ @Test(groups = "Functional")
+ public void testCalculateWrappedGeometry_noAnnotations()
+ {
+ AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+ "examples/uniref50.fa", DataSourceType.FILE);
+ AlignViewport av = af.getViewport();
+ AlignmentI al = av.getAlignment();
+ assertEquals(al.getWidth(), 157);
+ assertEquals(al.getHeight(), 15);
+
+ av.setWrapAlignment(true);
+ av.getRanges().setStartEndSeq(0, 14);
+ av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
+ int charHeight = av.getCharHeight();
+ int charWidth = av.getCharWidth();
+ assertEquals(charHeight, 17);
+ assertEquals(charWidth, 12);
+
+ SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
+
+ /*
+ * first with scales above, left, right
+ */
+ av.setShowAnnotation(false);
+ av.setScaleAboveWrapped(true);
+ av.setScaleLeftWrapped(true);
+ av.setScaleRightWrapped(true);
- FontMetrics fm = SwingUtilities2.getFontMetrics(testee, av.getFont());
++ FontMetrics fm = testee.getFontMetrics(av.getFont());
+ int labelWidth = fm.stringWidth("000") + charWidth;
+ assertEquals(labelWidth, 39); // 3 x 9 + charWidth
+
+ /*
+ * width 400 pixels leaves (400 - 2*labelWidth) for residue columns
+ * take the whole multiple of character widths
+ */
+ int canvasWidth = 400;
+ int canvasHeight = 300;
+ int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth;
+ int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
+ assertEquals(wrappedWidth, residueColumns);
+ assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth);
+ assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth);
+ assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"),
+ 2 * charHeight);
+ int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx");
+ assertEquals(repeatingHeight, charHeight * (2 + al.getHeight()));
+ assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
+
+ /*
+ * repeat height is 17 * (2 + 15) = 289
+ * make canvas height 2 * 289 + 3 * charHeight so just enough to
+ * draw 2 widths and the first sequence of a third
+ */
+ canvasHeight = charHeight * (17 * 2 + 3);
+ testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
+ assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
+
+ /*
+ * reduce canvas height by 1 pixel - should not be enough height
+ * to draw 3 widths
+ */
+ canvasHeight -= 1;
+ testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
+ assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
+
+ /*
+ * turn off scale above - can now fit in 2 and a bit widths
+ */
+ av.setScaleAboveWrapped(false);
+ testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
+ assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
+
+ /*
+ * reduce height to enough for 2 widths and not quite a third
+ * i.e. two repeating heights + spacer + sequence - 1 pixel
+ */
+ canvasHeight = charHeight * (16 * 2 + 2) - 1;
+ testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
+ assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
+
+ /*
+ * make canvas width enough for scales and 20 residues
+ */
+ canvasWidth = 2 * labelWidth + 20 * charWidth;
+ wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
+ canvasHeight);
+ assertEquals(wrappedWidth, 20);
+
+ /*
+ * reduce width by 1 pixel - rounds down to 19 residues
+ */
+ canvasWidth -= 1;
+ wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
+ canvasHeight);
+ assertEquals(wrappedWidth, 19);
+
+ /*
+ * turn off West scale - adds labelWidth (39) to available for residues
+ * which with the 11 remainder makes 50 which is 4 more charWidths rem 2
+ */
+ av.setScaleLeftWrapped(false);
+ wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
+ canvasHeight);
+ assertEquals(wrappedWidth, 23);
+
+ /*
+ * add 10 pixels to width to fit in another whole residue column
+ */
+ canvasWidth += 9;
+ wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
+ canvasHeight);
+ assertEquals(wrappedWidth, 23);
+ canvasWidth += 1;
+ wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
+ canvasHeight);
+ assertEquals(wrappedWidth, 24);
+
+ /*
+ * turn off East scale to gain 39 more pixels (3 columns remainder 3)
+ */
+ av.setScaleRightWrapped(false);
+ wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
+ canvasHeight);
+ assertEquals(wrappedWidth, 27);
+
+ /*
+ * add 9 pixels to width to gain a residue column
+ */
+ canvasWidth += 8;
+ wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
+ canvasHeight);
+ assertEquals(wrappedWidth, 27);
+ canvasWidth += 1;
+ wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
+ canvasHeight);
+ assertEquals(wrappedWidth, 28);
+
+ /*
+ * now West but not East scale - lose 39 pixels or 4 columns
+ */
+ av.setScaleLeftWrapped(true);
+ wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
+ canvasHeight);
+ assertEquals(wrappedWidth, 24);
+
+ /*
+ * adding 3 pixels to width regains one column
+ */
+ canvasWidth += 2;
+ wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
+ canvasHeight);
+ assertEquals(wrappedWidth, 24);
+ canvasWidth += 1;
+ wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
+ canvasHeight);
+ assertEquals(wrappedWidth, 25);
+
+ /*
+ * turn off scales left and right, make width exactly 157 columns
+ */
+ av.setScaleLeftWrapped(false);
+ canvasWidth = al.getWidth() * charWidth;
+ testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
+ assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
+ }
+
+ /**
+ * Test the method that computes wrapped width in residues, height of wrapped
+ * widths in pixels, and the number of widths visible
+ */
+ @Test(groups = "Functional")
+ public void testCalculateWrappedGeometry_withAnnotations()
+ {
+ AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+ "examples/uniref50.fa", DataSourceType.FILE);
+ AlignViewport av = af.getViewport();
+ AlignmentI al = av.getAlignment();
+ assertEquals(al.getWidth(), 157);
+ assertEquals(al.getHeight(), 15);
+
+ av.setWrapAlignment(true);
+ av.getRanges().setStartEndSeq(0, 14);
+ av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
+ int charHeight = av.getCharHeight();
+ int charWidth = av.getCharWidth();
+ assertEquals(charHeight, 17);
+ assertEquals(charWidth, 12);
+
+ SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
+
+ /*
+ * first with scales above, left, right
+ */
+ av.setShowAnnotation(true);
+ av.setScaleAboveWrapped(true);
+ av.setScaleLeftWrapped(true);
+ av.setScaleRightWrapped(true);
- FontMetrics fm = SwingUtilities2.getFontMetrics(testee, av.getFont());
++ FontMetrics fm = testee.getFontMetrics(av.getFont());
+ int labelWidth = fm.stringWidth("000") + charWidth;
+ assertEquals(labelWidth, 39); // 3 x 9 + charWidth
+ int annotationHeight = testee.getAnnotationHeight();
+
+ /*
+ * width 400 pixels leaves (400 - 2*labelWidth) for residue columns
+ * take the whole multiple of character widths
+ */
+ int canvasWidth = 400;
+ int canvasHeight = 300;
+ int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth;
+ int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
+ assertEquals(wrappedWidth, residueColumns);
+ assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth);
+ assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth);
+ assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"),
+ 2 * charHeight);
+ int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx");
+ assertEquals(repeatingHeight, charHeight * (2 + al.getHeight())
+ + annotationHeight);
+ assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
+
+ /*
+ * repeat height is 17 * (2 + 15) = 289 + annotationHeight = 507
+ * make canvas height 2 * 289 + 3 * charHeight so just enough to
+ * draw 2 widths and the first sequence of a third
+ */
+ canvasHeight = charHeight * (17 * 2 + 3) + 2 * annotationHeight;
+ testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
+ assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
+
+ /*
+ * reduce canvas height by 1 pixel - should not be enough height
+ * to draw 3 widths
+ */
+ canvasHeight -= 1;
+ testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
+ assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
+
+ /*
+ * turn off scale above - can now fit in 2 and a bit widths
+ */
+ av.setScaleAboveWrapped(false);
+ testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
+ assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
+
+ /*
+ * reduce height to enough for 2 widths and not quite a third
+ * i.e. two repeating heights + spacer + sequence - 1 pixel
+ */
+ canvasHeight = charHeight * (16 * 2 + 2) + 2 * annotationHeight - 1;
+ testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
+ assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
+
+ /*
+ * add 1 pixel to height - should now get 3 widths drawn
+ */
+ canvasHeight += 1;
+ testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
+ assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
+ }
+ }