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