JAL-2864 more generous wait for gc() to complete in test
[jalview.git] / src / jalview / io / gff / SequenceOntologyLite.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.io.gff;
22
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28
29 /**
30  * An implementation of SequenceOntologyI that hard codes terms of interest.
31  *
32  * Use this in unit testing by calling SequenceOntology.setInstance(new
33  * SequenceOntologyLite()).
34  * 
35  * May also become a stand-in for SequenceOntology in the applet if we want to
36  * avoid the additional jars needed for parsing the full SO.
37  * 
38  * @author gmcarstairs
39  *
40  */
41 public class SequenceOntologyLite implements SequenceOntologyI
42 {
43   /*
44    * initial selection of types of interest when processing Ensembl features
45    * NB unlike the full SequenceOntology we don't traverse indirect
46    * child-parent relationships here so e.g. need to list every sub-type
47    * of gene (direct or indirect) that is of interest
48    */
49   // @formatter:off
50   private final String[][] TERMS = new String[][] {
51
52     /*
53      * gene sub-types:
54      */
55     { "gene", "gene" }, 
56     { "ncRNA_gene", "gene" }, 
57     { "snRNA_gene", "gene" },
58     { "miRNA_gene", "gene" },
59     { "lincRNA_gene", "gene" },
60     { "rRNA_gene", "gene" },
61     
62     /*
63      * transcript sub-types:
64      */
65     { "transcript", "transcript" }, 
66     { "mature_transcript", "transcript" }, 
67     { "processed_transcript", "transcript" }, 
68     { "aberrant_processed_transcript", "transcript" },
69     { "ncRNA", "transcript" },
70     { "snRNA", "transcript" },
71     { "miRNA", "transcript" },
72     { "lincRNA", "transcript" },
73     { "rRNA", "transcript" },
74     { "mRNA", "transcript" },
75     // there are many more sub-types of ncRNA...
76     
77     /*
78      * sequence_variant sub-types:
79      */
80     { "sequence_variant", "sequence_variant" },
81     { "feature_variant", "sequence_variant" },
82     { "gene_variant", "sequence_variant" },
83     // NB Ensembl uses NMD_transcript_variant as if a 'transcript'
84     // but we model it here correctly as per the SO
85     { "NMD_transcript_variant", "sequence_variant" },
86     { "transcript_variant", "sequence_variant" },
87     { "structural_variant", "sequence_variant" },
88     
89     /*
90      * no sub-types of exon or CDS yet seen in Ensembl
91      * some added here for testing purposes
92      */
93     { "exon", "exon" },
94     { "coding_exon", "exon" },
95     { "CDS", "CDS" },
96     { "CDS_predicted", "CDS" },
97     
98     /*
99      * terms used in exonerate or PASA GFF
100      */
101     { "protein_match", "protein_match"},
102     { "nucleotide_match", "nucleotide_match"},
103     { "cDNA_match", "nucleotide_match"},
104     
105     /*
106      * used in InterProScan GFF
107      */
108     { "polypeptide", "polypeptide" }
109   };
110   // @formatter:on
111
112   /*
113    * hard-coded list of any parents (direct or indirect) 
114    * that we care about for a term
115    */
116   private Map<String, List<String>> parents;
117
118   private List<String> termsFound;
119
120   private List<String> termsNotFound;
121
122   public SequenceOntologyLite()
123   {
124     termsFound = new ArrayList<String>();
125     termsNotFound = new ArrayList<String>();
126     loadStaticData();
127   }
128
129   /**
130    * Loads hard-coded data into a lookup table of {term, {list_of_parents}}
131    */
132   private void loadStaticData()
133   {
134     parents = new HashMap<String, List<String>>();
135     for (String[] pair : TERMS)
136     {
137       List<String> p = parents.get(pair[0]);
138       if (p == null)
139       {
140         p = new ArrayList<String>();
141         parents.put(pair[0], p);
142       }
143       p.add(pair[1]);
144     }
145   }
146
147   /**
148    * Answers true if 'child' isA 'parent' (including equality). In this
149    * implementation, based only on hard-coded values.
150    */
151   @Override
152   public boolean isA(String child, String parent)
153   {
154     if (child == null || parent == null)
155     {
156       return false;
157     }
158     if (child.equals(parent))
159     {
160       termFound(child);
161       return true;
162     }
163
164     List<String> p = parents.get(child);
165     if (p == null)
166     {
167       termNotFound(child);
168       return false;
169     }
170     termFound(child);
171     if (p.contains(parent))
172     {
173       return true;
174     }
175     return false;
176   }
177
178   /**
179    * Records a valid term queried for, for reporting purposes
180    * 
181    * @param term
182    */
183   private void termFound(String term)
184   {
185     if (!termsFound.contains(term))
186     {
187       synchronized (termsFound)
188       {
189         termsFound.add(term);
190       }
191     }
192   }
193
194   /**
195    * Records an invalid term queried for, for reporting purposes
196    * 
197    * @param term
198    */
199   private void termNotFound(String term)
200   {
201     synchronized (termsNotFound)
202     {
203       if (!termsNotFound.contains(term))
204       {
205         // suppress logging here as it reports Uniprot sequence features
206         // (which do not use SO terms) when auto-configuring feature colours
207         // System.out.println("SO term " + term
208         // + " not known - add to model if needed in "
209         // + getClass().getName());
210         termsNotFound.add(term);
211       }
212     }
213   }
214
215   /**
216    * Sorts (case-insensitive) and returns the list of valid terms queried for
217    */
218   @Override
219   public List<String> termsFound()
220   {
221     synchronized (termsFound)
222     {
223       Collections.sort(termsFound, String.CASE_INSENSITIVE_ORDER);
224       return termsFound;
225     }
226   }
227
228   /**
229    * Sorts (case-insensitive) and returns the list of invalid terms queried for
230    */
231   @Override
232   public List<String> termsNotFound()
233   {
234     synchronized (termsNotFound)
235     {
236       Collections.sort(termsNotFound, String.CASE_INSENSITIVE_ORDER);
237       return termsNotFound;
238     }
239   }
240 }