Clover icon

Coverage Report

  1. Project Clover database Mon Nov 18 2024 09:38:20 GMT
  2. Package jalview.datamodel.features

File FeatureAttributes.java

 

Coverage histogram

../../../img/srcFileCovDistChart5.png
33% of files have more coverage

Code metrics

64
96
16
3
419
240
57
0.59
6
5.33
3.56

Classes

Class Line # Actions
FeatureAttributes 35 73 38
0.3720930237.2%
FeatureAttributes.Datatype 37 0 0
-1.0 -
FeatureAttributes.AttributeData 84 23 19
0.5957446759.6%
 

Contributing tests

This file is covered by 46 tests. .

Source view

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.datamodel.features;
22   
23    import java.util.ArrayList;
24    import java.util.Collections;
25    import java.util.Comparator;
26    import java.util.HashMap;
27    import java.util.List;
28    import java.util.Map;
29    import java.util.Map.Entry;
30    import java.util.TreeMap;
31   
32    /**
33    * A singleton class to hold the set of attributes known for each feature type
34    */
 
35    public class FeatureAttributes
36    {
 
37    public enum Datatype
38    {
39    Character, Number, Mixed
40    }
41   
42    private static FeatureAttributes instance = new FeatureAttributes();
43   
44    /*
45    * map, by feature type, of a map, by attribute name, of
46    * attribute description and min-max range (if known)
47    */
48    private Map<String, Map<String[], AttributeData>> attributes;
49   
50    /*
51    * a case-insensitive comparator so that attributes are ordered e.g.
52    * AC
53    * af
54    * CSQ:AFR_MAF
55    * CSQ:Allele
56    */
57    private Comparator<String[]> comparator = new Comparator<String[]>()
58    {
 
59  3954 toggle @Override
60    public int compare(String[] o1, String[] o2)
61    {
62  3954 int i = 0;
63  7649 while (i < o1.length || i < o2.length)
64    {
65  3959 if (o2.length <= i)
66    {
67  0 return o1.length <= i ? 0 : 1;
68    }
69  3959 if (o1.length <= i)
70    {
71  0 return -1;
72    }
73  3959 int comp = String.CASE_INSENSITIVE_ORDER.compare(o1[i], o2[i]);
74  3959 if (comp != 0)
75    {
76  264 return comp;
77    }
78  3695 i++;
79    }
80  3690 return 0; // same length and all matched
81    }
82    };
83   
 
84    private class AttributeData
85    {
86    /*
87    * description(s) for this attribute, if known
88    * (different feature source might have differing descriptions)
89    */
90    List<String> description;
91   
92    /*
93    * minimum value (of any numeric values recorded)
94    */
95    float min = 0f;
96   
97    /*
98    * maximum value (of any numeric values recorded)
99    */
100    float max = 0f;
101   
102    /*
103    * flag is set true if any numeric value is detected for this attribute
104    */
105    boolean hasValue = false;
106   
107    Datatype type;
108   
109    /**
110    * Note one instance of this attribute, recording unique, non-null
111    * descriptions, and the min/max of any numerical values
112    *
113    * @param desc
114    * @param value
115    */
 
116  3722 toggle void addInstance(String desc, String value)
117    {
118  3722 addDescription(desc);
119   
120  3722 if (value != null)
121    {
122  3722 value = value.trim();
123   
124    /*
125    * Parse numeric value unless we have previously
126    * seen text data for this attribute type
127    */
128  3722 if (type == null || type == Datatype.Number)
129    {
130  138 try
131    {
132  138 float f = Float.valueOf(value);
133  92 min = hasValue ? Math.min(min, f) : f;
134  92 max = hasValue ? Math.max(max, f) : f;
135  92 hasValue = true;
136  92 type = (type == null || type == Datatype.Number)
137    ? Datatype.Number
138    : Datatype.Mixed;
139    } catch (NumberFormatException e)
140    {
141    /*
142    * non-numeric data: treat attribute as Character (or Mixed)
143    */
144  46 type = (type == null || type == Datatype.Character)
145    ? Datatype.Character
146    : Datatype.Mixed;
147  46 min = 0f;
148  46 max = 0f;
149  46 hasValue = false;
150    }
151    }
152    }
153    }
154   
155    /**
156    * Answers the description of the attribute, if recorded and unique, or null
157    * if either no, or more than description is recorded
158    *
159    * @return
160    */
 
161  0 toggle public String getDescription()
162    {
163  0 if (description != null && description.size() == 1)
164    {
165  0 return description.get(0);
166    }
167  0 return null;
168    }
169   
 
170  0 toggle public Datatype getType()
171    {
172  0 return type;
173    }
174   
175    /**
176    * Adds the given description to the list of known descriptions (without
177    * duplication)
178    *
179    * @param desc
180    */
 
181  3722 toggle public void addDescription(String desc)
182    {
183  3722 if (desc != null)
184    {
185  0 if (description == null)
186    {
187  0 description = new ArrayList<>();
188    }
189  0 if (!description.contains(desc))
190    {
191  0 description.add(desc);
192    }
193    }
194    }
195    }
196   
197    /**
198    * Answers the singleton instance of this class
199    *
200    * @return
201    */
 
202  3723 toggle public static FeatureAttributes getInstance()
203    {
204  3723 return instance;
205    }
206   
 
207  1 toggle private FeatureAttributes()
208    {
209  1 attributes = new HashMap<>();
210    }
211   
212    /**
213    * Answers the attribute names known for the given feature type, in
214    * alphabetical order (not case sensitive), or an empty set if no attributes
215    * are known. An attribute name is typically 'simple' e.g. "AC", but may be
216    * 'compound' e.g. {"CSQ", "Allele"} where a feature has map-valued attributes
217    *
218    * @param featureType
219    * @return
220    */
 
221  0 toggle public List<String[]> getAttributes(String featureType)
222    {
223  0 if (!attributes.containsKey(featureType))
224    {
225  0 return Collections.<String[]> emptyList();
226    }
227   
228  0 return new ArrayList<>(attributes.get(featureType).keySet());
229    }
230   
231    /**
232    * Answers true if at least one attribute is known for the given feature type,
233    * else false
234    *
235    * @param featureType
236    * @return
237    */
 
238  0 toggle public boolean hasAttributes(String featureType)
239    {
240  0 if (attributes.containsKey(featureType))
241    {
242  0 if (!attributes.get(featureType).isEmpty())
243    {
244  0 return true;
245    }
246    }
247  0 return false;
248    }
249   
250    /**
251    * Records the given attribute name and description for the given feature
252    * type, and updates the min-max for any numeric value
253    *
254    * @param featureType
255    * @param description
256    * @param value
257    * @param attName
258    */
 
259  3734 toggle public void addAttribute(String featureType, String description,
260    Object value, String... attName)
261    {
262  3734 if (featureType == null || attName == null)
263    {
264  0 return;
265    }
266   
267    /*
268    * if attribute value is a map, drill down one more level to
269    * record its sub-fields
270    */
271  3734 if (value instanceof Map<?, ?>)
272    {
273  12 for (Entry<?, ?> entry : ((Map<?, ?>) value).entrySet())
274    {
275  12 String[] attNames = new String[attName.length + 1];
276  12 System.arraycopy(attName, 0, attNames, 0, attName.length);
277  12 attNames[attName.length] = entry.getKey().toString();
278  12 addAttribute(featureType, description, entry.getValue(), attNames);
279    }
280  12 return;
281    }
282   
283  3722 String valueAsString = value.toString();
284  3722 Map<String[], AttributeData> atts = attributes.get(featureType);
285  3722 if (atts == null)
286    {
287  30 atts = new TreeMap<>(comparator);
288  30 attributes.put(featureType, atts);
289    }
290  3722 AttributeData attData = atts.get(attName);
291  3722 if (attData == null)
292    {
293  62 attData = new AttributeData();
294  62 atts.put(attName, attData);
295    }
296  3722 attData.addInstance(description, valueAsString);
297    }
298   
299    /**
300    * Answers the description of the given attribute for the given feature type,
301    * if known and unique, else null
302    *
303    * @param featureType
304    * @param attName
305    * @return
306    */
 
307  0 toggle public String getDescription(String featureType, String... attName)
308    {
309  0 String desc = null;
310  0 Map<String[], AttributeData> atts = attributes.get(featureType);
311  0 if (atts != null)
312    {
313  0 AttributeData attData = atts.get(attName);
314  0 if (attData != null)
315    {
316  0 desc = attData.getDescription();
317    }
318    }
319  0 return desc;
320    }
321   
322    /**
323    * Answers the [min, max] value range of the given attribute for the given
324    * feature type, if known, else null. Attributes with a mixture of text and
325    * numeric values are considered text (do not return a min-max range).
326    *
327    * @param featureType
328    * @param attName
329    * @return
330    */
 
331  0 toggle public float[] getMinMax(String featureType, String... attName)
332    {
333  0 Map<String[], AttributeData> atts = attributes.get(featureType);
334  0 if (atts != null)
335    {
336  0 AttributeData attData = atts.get(attName);
337  0 if (attData != null && attData.hasValue)
338    {
339  0 return new float[] { attData.min, attData.max };
340    }
341    }
342  0 return null;
343    }
344   
345    /**
346    * Records the given attribute description for the given feature type
347    *
348    * @param featureType
349    * @param attName
350    * @param description
351    */
 
352  0 toggle public void addDescription(String featureType, String description,
353    String... attName)
354    {
355  0 if (featureType == null || attName == null)
356    {
357  0 return;
358    }
359   
360  0 Map<String[], AttributeData> atts = attributes.get(featureType);
361  0 if (atts == null)
362    {
363  0 atts = new TreeMap<>(comparator);
364  0 attributes.put(featureType, atts);
365    }
366  0 AttributeData attData = atts.get(attName);
367  0 if (attData == null)
368    {
369  0 attData = new AttributeData();
370  0 atts.put(attName, attData);
371    }
372  0 attData.addDescription(description);
373    }
374   
375    /**
376    * Answers the datatype of the feature, which is one of Character, Number or
377    * Mixed (or null if not known), as discovered from values recorded.
378    *
379    * @param featureType
380    * @param attName
381    * @return
382    */
 
383  0 toggle public Datatype getDatatype(String featureType, String... attName)
384    {
385  0 Map<String[], AttributeData> atts = attributes.get(featureType);
386  0 if (atts != null)
387    {
388  0 AttributeData attData = atts.get(attName);
389  0 if (attData != null)
390    {
391  0 return attData.getType();
392    }
393    }
394  0 return null;
395    }
396   
397    /**
398    * Resets all attribute metadata
399    */
 
400  1 toggle public void clear()
401    {
402  1 attributes.clear();
403    }
404   
405    /**
406    * Resets attribute metadata for one feature type
407    *
408    * @param featureType
409    */
 
410  0 toggle public void clear(String featureType)
411    {
412  0 Map<String[], AttributeData> map = attributes.get(featureType);
413  0 if (map != null)
414    {
415  0 map.clear();
416    }
417   
418    }
419    }