c0ae971043d2e11afe0e02c75ca596c0020e616e
[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 encountered in Ensembl
59      * a few added here for testing purposes
60      */
61     { "exon", "exon" },
62     { "coding_exon", "exon" },
63     { "CDS", "CDS" },
64     { "CDS_predicted", "CDS" },
65     
66     /*
67      * used in exonerate GFF
68      */
69     { "protein_match", "protein_match"},
70     { "nucleotide_match", "nucleotide_match"},
71     
72     /*
73      * used in InterProScan GFF
74      */
75     { "polypeptide", "polypeptide" }
76   };
77   // @formatter:on
78
79   /*
80    * hard-coded list of any parents (direct or indirect) 
81    * that we care about for a term
82    */
83   private Map<String, List<String>> parents;
84
85   private List<String> termsFound;
86
87   private List<String> termsNotFound;
88
89   public SequenceOntologyLite()
90   {
91     termsFound = new ArrayList<String>();
92     termsNotFound = new ArrayList<String>();
93     loadStaticData();
94   }
95
96   /**
97    * Loads hard-coded data into a lookup table of {term, {list_of_parents}}
98    */
99   private void loadStaticData()
100   {
101     parents = new HashMap<String, List<String>>();
102     for (String [] pair : TERMS) {
103       List<String> p = parents.get(pair[0]);
104       if (p == null)
105       {
106         p = new ArrayList<String>();
107         parents.put(pair[0], p);
108       }
109       p.add(pair[1]);
110     }
111   }
112
113   /**
114    * Answers true if 'child' isA 'parent' (including equality). In this
115    * implementation, based only on hard-coded values.
116    */
117   @Override
118   public boolean isA(String child, String parent)
119   {
120     if (child == null || parent == null)
121     {
122       return false;
123     }
124     if (child.equals(parent))
125     {
126       termFound(child);
127       return true;
128     }
129
130     List<String> p = parents.get(child);
131     if (p == null)
132     {
133       termNotFound(child);
134       return false;
135     }
136     termFound(child);
137     if (p.contains(parent))
138     {
139       return true;
140     }
141     return false;
142   }
143
144   /**
145    * Records a valid term queried for, for reporting purposes
146    * 
147    * @param term
148    */
149   private void termFound(String term)
150   {
151     if (!termsFound.contains(term))
152     {
153       synchronized (termsFound)
154       {
155         termsFound.add(term);
156       }
157     }
158   }
159
160   /**
161    * Records an invalid term queried for, for reporting purposes
162    * 
163    * @param term
164    */
165   private void termNotFound(String term)
166   {
167     synchronized (termsNotFound)
168     {
169       if (!termsNotFound.contains(term))
170       {
171         System.out.println("SO term " + term
172                 + " not known - either invalid or needs modelled in "
173                 + getClass().getName());
174         termsNotFound.add(term);
175       }
176     }
177   }
178
179   /**
180    * Sorts (case-insensitive) and returns the list of valid terms queried for
181    */
182   @Override
183   public List<String> termsFound()
184   {
185     synchronized (termsFound)
186     {
187       Collections.sort(termsFound, String.CASE_INSENSITIVE_ORDER);
188       return termsFound;
189     }
190   }
191
192   /**
193    * Sorts (case-insensitive) and returns the list of invalid terms queried for
194    */
195   @Override
196   public List<String> termsNotFound()
197   {
198     synchronized (termsNotFound)
199     {
200       Collections.sort(termsNotFound, String.CASE_INSENSITIVE_ORDER);
201       return termsNotFound;
202     }
203   }
204 }