JAL-1191 additions to SOLite, tweak to test
[jalview.git] / src / jalview / io / gff / SequenceOntologyLite.java
1 package jalview.io.gff;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.HashMap;
6 import java.util.List;
7 import java.util.Map;
8
9 /**
10  * An implementation of SequenceOntologyI that hard codes terms of interest.
11  *
12  * Use this in unit testing by calling SequenceOntology.setInstance(new
13  * SequenceOntologyLite()).
14  * 
15  * May also become a stand-in for SequenceOntology in the applet if we want to
16  * avoid the additional jars needed for parsing the full SO.
17  * 
18  * @author gmcarstairs
19  *
20  */
21 public class SequenceOntologyLite implements SequenceOntologyI
22 {
23   /*
24    * initial selection of types of interest when processing Ensembl features
25    * NB unlike the full SequenceOntology we don't traverse indirect
26    * child-parent relationships here so e.g. need to list every sub-type
27    * of gene (direct or indirect) that is of interest
28    */
29   // @formatter:off
30   private final String[][] TERMS = new String[][] {
31
32     /*
33      * gene sub-types:
34      */
35     { "gene", "gene" }, 
36     { "ncRNA_gene", "gene" }, 
37     { "snRNA_gene", "gene" },
38     { "miRNA_gene", "gene" },
39     { "lincRNA_gene", "gene" },
40     
41     /*
42      * transcript sub-types:
43      */
44     { "transcript", "transcript" }, 
45     { "mature_transcript", "transcript" }, 
46     { "processed_transcript", "transcript" }, 
47     { "aberrant_processed_transcript", "transcript" },
48     { "ncRNA", "transcript" },
49     { "snRNA", "transcript" },
50     { "miRNA", "transcript" },
51     { "lincRNA", "transcript" },
52     // there are many more sub-types of ncRNA...
53     
54     /*
55      * sequence_variant sub-types:
56      */
57     { "sequence_variant", "sequence_variant" },
58     { "feature_variant", "sequence_variant" },
59     { "gene_variant", "sequence_variant" },
60     // NB Ensembl uses NMD_transcript_variant as if a 'transcript'
61     // but we model it here correctly as per the SO
62     { "NMD_transcript_variant", "sequence_variant" },
63     { "transcript_variant", "sequence_variant" },
64     { "structural_variant", "sequence_variant" },
65     
66     /*
67      * no sub-types of exon or CDS yet seen in Ensembl
68      * some added here for testing purposes
69      */
70     { "exon", "exon" },
71     { "coding_exon", "exon" },
72     { "CDS", "CDS" },
73     { "CDS_predicted", "CDS" },
74     
75     /*
76      * terms used in exonerate or PASA GFF
77      */
78     { "protein_match", "protein_match"},
79     { "nucleotide_match", "nucleotide_match"},
80     { "cDNA_match", "nucleotide_match"},
81     
82     /*
83      * used in InterProScan GFF
84      */
85     { "polypeptide", "polypeptide" }
86   };
87   // @formatter:on
88
89   /*
90    * hard-coded list of any parents (direct or indirect) 
91    * that we care about for a term
92    */
93   private Map<String, List<String>> parents;
94
95   private List<String> termsFound;
96
97   private List<String> termsNotFound;
98
99   public SequenceOntologyLite()
100   {
101     termsFound = new ArrayList<String>();
102     termsNotFound = new ArrayList<String>();
103     loadStaticData();
104   }
105
106   /**
107    * Loads hard-coded data into a lookup table of {term, {list_of_parents}}
108    */
109   private void loadStaticData()
110   {
111     parents = new HashMap<String, List<String>>();
112     for (String [] pair : TERMS) {
113       List<String> p = parents.get(pair[0]);
114       if (p == null)
115       {
116         p = new ArrayList<String>();
117         parents.put(pair[0], p);
118       }
119       p.add(pair[1]);
120     }
121   }
122
123   /**
124    * Answers true if 'child' isA 'parent' (including equality). In this
125    * implementation, based only on hard-coded values.
126    */
127   @Override
128   public boolean isA(String child, String parent)
129   {
130     if (child == null || parent == null)
131     {
132       return false;
133     }
134     if (child.equals(parent))
135     {
136       termFound(child);
137       return true;
138     }
139
140     List<String> p = parents.get(child);
141     if (p == null)
142     {
143       termNotFound(child);
144       return false;
145     }
146     termFound(child);
147     if (p.contains(parent))
148     {
149       return true;
150     }
151     return false;
152   }
153
154   /**
155    * Records a valid term queried for, for reporting purposes
156    * 
157    * @param term
158    */
159   private void termFound(String term)
160   {
161     if (!termsFound.contains(term))
162     {
163       synchronized (termsFound)
164       {
165         termsFound.add(term);
166       }
167     }
168   }
169
170   /**
171    * Records an invalid term queried for, for reporting purposes
172    * 
173    * @param term
174    */
175   private void termNotFound(String term)
176   {
177     synchronized (termsNotFound)
178     {
179       if (!termsNotFound.contains(term))
180       {
181         System.out.println("SO term " + term
182                 + " not known - may be invalid, or model if needed in "
183                 + getClass().getName());
184         termsNotFound.add(term);
185       }
186     }
187   }
188
189   /**
190    * Sorts (case-insensitive) and returns the list of valid terms queried for
191    */
192   @Override
193   public List<String> termsFound()
194   {
195     synchronized (termsFound)
196     {
197       Collections.sort(termsFound, String.CASE_INSENSITIVE_ORDER);
198       return termsFound;
199     }
200   }
201
202   /**
203    * Sorts (case-insensitive) and returns the list of invalid terms queried for
204    */
205   @Override
206   public List<String> termsNotFound()
207   {
208     synchronized (termsNotFound)
209     {
210       Collections.sort(termsNotFound, String.CASE_INSENSITIVE_ORDER);
211       return termsNotFound;
212     }
213   }
214 }