JAL-1705 JAL-1191 SequenceOntologyLite added as hard-coded alternative
[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    */
26   // @formatter:off
27   private final String[][] TERMS = new String[][] {
28
29     /*
30      * gene sub-types:
31      */
32     { "gene", "gene" }, 
33     { "ncRNA_gene", "gene" }, 
34     { "snRNA_gene", "gene" },
35     
36     /*
37      * transcript sub-types:
38      */
39     { "transcript", "transcript" }, 
40     { "mature_transcript", "transcript" }, 
41     { "ncRNA", "transcript" },
42     { "snRNA", "transcript" },
43     { "aberrant_processed_transcript", "transcript" },
44     
45     /*
46      * sequence_variant sub-types:
47      */
48     { "sequence_variant", "sequence_variant" },
49     { "feature_variant", "sequence_variant" },
50     { "gene_variant", "sequence_variant" },
51     // NB Ensembl uses NMD_transcript_variant as if a 'transcript'
52     // but we model it here correctly as per the SO
53     { "NMD_transcript_variant", "sequence_variant" },
54     { "transcript_variant", "sequence_variant" },
55     { "structural_variant", "sequence_variant" },
56     
57     /*
58      * no sub-types of exon or CDS yet encountered; add if needed
59      */
60     { "exon", "exon" },
61     { "CDS", "CDS" }
62   };
63   // @formatter:on
64
65   /*
66    * hard-coded list of any parents (direct or indirect) 
67    * that we care about for a term
68    */
69   private Map<String, List<String>> parents;
70
71   private List<String> termsFound;
72
73   private List<String> termsNotFound;
74
75   public SequenceOntologyLite()
76   {
77     termsFound = new ArrayList<String>();
78     termsNotFound = new ArrayList<String>();
79     loadStaticData();
80   }
81
82   /**
83    * Loads hard-coded data into a lookup table of {term, {list_of_parents}}
84    */
85   private void loadStaticData()
86   {
87     parents = new HashMap<String, List<String>>();
88     for (String [] pair : TERMS) {
89       List<String> p = parents.get(pair[0]);
90       if (p == null)
91       {
92         p = new ArrayList<String>();
93         parents.put(pair[0], p);
94       }
95       p.add(pair[1]);
96     }
97   }
98
99   /**
100    * Answers true if 'child' isA 'parent' (including equality). In this
101    * implementation, based only on hard-coded values.
102    */
103   @Override
104   public boolean isA(String child, String parent)
105   {
106     if (child == null || parent == null)
107     {
108       return false;
109     }
110     if (child.equals(parent))
111     {
112       termFound(child);
113       return true;
114     }
115
116     List<String> p = parents.get(child);
117     if (p == null)
118     {
119       termNotFound(child);
120       return false;
121     }
122     termFound(child);
123     if (p.contains(parent))
124     {
125       return true;
126     }
127     return false;
128   }
129
130   /**
131    * Records a valid term queried for, for reporting purposes
132    * 
133    * @param term
134    */
135   private void termFound(String term)
136   {
137     if (!termsFound.contains(term))
138     {
139       synchronized (termsFound)
140       {
141         termsFound.add(term);
142       }
143     }
144   }
145
146   /**
147    * Records an invalid term queried for, for reporting purposes
148    * 
149    * @param term
150    */
151   private void termNotFound(String term)
152   {
153     synchronized (termsNotFound)
154     {
155       if (!termsNotFound.contains(term))
156       {
157         System.out.println("SO term " + term
158                 + " not known - either invalid or needs modelled in "
159                 + getClass().getName());
160         termsNotFound.add(term);
161       }
162     }
163   }
164
165   /**
166    * Sorts (case-insensitive) and returns the list of valid terms queried for
167    */
168   @Override
169   public List<String> termsFound()
170   {
171     synchronized (termsFound)
172     {
173       Collections.sort(termsFound, String.CASE_INSENSITIVE_ORDER);
174       return termsFound;
175     }
176   }
177
178   /**
179    * Sorts (case-insensitive) and returns the list of invalid terms queried for
180    */
181   @Override
182   public List<String> termsNotFound()
183   {
184     synchronized (termsNotFound)
185     {
186       Collections.sort(termsNotFound, String.CASE_INSENSITIVE_ORDER);
187       return termsNotFound;
188     }
189   }
190 }