Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.datamodel.features

File FeatureAttributes.java

 

Coverage histogram

../../../img/srcFileCovDistChart9.png
12% of files have more coverage

Code metrics

64
96
16
3
417
240
57
0.59
6
5.33
3.56

Classes

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

Contributing tests

This file is covered by 80 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  1785089 toggle @Override
60    public int compare(String[] o1, String[] o2)
61    {
62  1785089 int i = 0;
63  2306438 while (i < o1.length || i < o2.length)
64    {
65  1785597 if (o2.length <= i)
66    {
67  1 return o1.length <= i ? 0 : 1;
68    }
69  1785596 if (o1.length <= i)
70    {
71  1 return -1;
72    }
73  1785595 int comp = String.CASE_INSENSITIVE_ORDER.compare(o1[i], o2[i]);
74  1785595 if (comp != 0)
75    {
76  1264246 return comp;
77    }
78  521349 i++;
79    }
80  520841 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  520965 toggle void addInstance(String desc, String value)
117    {
118  520965 addDescription(desc);
119   
120  520965 if (value != null)
121    {
122  520965 value = value.trim();
123   
124    /*
125    * Parse numeric value unless we have previously
126    * seen text data for this attribute type
127    */
128  520965 if (type == null || type == Datatype.Number)
129    {
130  743 try
131    {
132  743 float f = Float.valueOf(value);
133  607 min = hasValue ? Math.min(min, f) : f;
134  607 max = hasValue ? Math.max(max, f) : f;
135  607 hasValue = true;
136  607 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  136 type = (type == null || type == Datatype.Character)
145    ? Datatype.Character
146    : Datatype.Mixed;
147  136 min = 0f;
148  136 max = 0f;
149  136 hasValue = false;
150    }
151    }
152    }
153    }
154   
155    /**
156    * Answers the description of the attribute, if recorded and unique, or null if either no, or more than description is recorded
157    * @return
158    */
 
159  2 toggle public String getDescription()
160    {
161  2 if (description != null && description.size() == 1)
162    {
163  1 return description.get(0);
164    }
165  1 return null;
166    }
167   
 
168  3 toggle public Datatype getType()
169    {
170  3 return type;
171    }
172   
173    /**
174    * Adds the given description to the list of known descriptions (without
175    * duplication)
176    *
177    * @param desc
178    */
 
179  520967 toggle public void addDescription(String desc)
180    {
181  520967 if (desc != null)
182    {
183  212 if (description == null)
184    {
185  39 description = new ArrayList<>();
186    }
187  212 if (!description.contains(desc))
188    {
189  40 description.add(desc);
190    }
191    }
192    }
193    }
194   
195    /**
196    * Answers the singleton instance of this class
197    *
198    * @return
199    */
 
200  520861 toggle public static FeatureAttributes getInstance()
201    {
202  520861 return instance;
203    }
204   
 
205  1 toggle private FeatureAttributes()
206    {
207  1 attributes = new HashMap<>();
208    }
209   
210    /**
211    * Answers the attribute names known for the given feature type, in
212    * alphabetical order (not case sensitive), or an empty set if no attributes
213    * are known. An attribute name is typically 'simple' e.g. "AC", but may be
214    * 'compound' e.g. {"CSQ", "Allele"} where a feature has map-valued attributes
215    *
216    * @param featureType
217    * @return
218    */
 
219  0 toggle public List<String[]> getAttributes(String featureType)
220    {
221  0 if (!attributes.containsKey(featureType))
222    {
223  0 return Collections.<String[]> emptyList();
224    }
225   
226  0 return new ArrayList<>(attributes.get(featureType).keySet());
227    }
228   
229    /**
230    * Answers true if at least one attribute is known for the given feature type,
231    * else false
232    *
233    * @param featureType
234    * @return
235    */
 
236  0 toggle public boolean hasAttributes(String featureType)
237    {
238  0 if (attributes.containsKey(featureType))
239    {
240  0 if (!attributes.get(featureType).isEmpty())
241    {
242  0 return true;
243    }
244    }
245  0 return false;
246    }
247   
248    /**
249    * Records the given attribute name and description for the given feature
250    * type, and updates the min-max for any numeric value
251    *
252    * @param featureType
253    * @param description
254    * @param value
255    * @param attName
256    */
 
257  521026 toggle public void addAttribute(String featureType, String description,
258    Object value, String... attName)
259    {
260  521026 if (featureType == null || attName == null)
261    {
262  0 return;
263    }
264   
265    /*
266    * if attribute value is a map, drill down one more level to
267    * record its sub-fields
268    */
269  521026 if (value instanceof Map<?, ?>)
270    {
271  61 for (Entry<?, ?> entry : ((Map<?, ?>) value).entrySet())
272    {
273  175 String[] attNames = new String[attName.length + 1];
274  175 System.arraycopy(attName, 0, attNames, 0, attName.length);
275  175 attNames[attName.length] = entry.getKey().toString();
276  175 addAttribute(featureType, description, entry.getValue(), attNames);
277    }
278  61 return;
279    }
280   
281  520965 String valueAsString = value.toString();
282  520965 Map<String[], AttributeData> atts = attributes.get(featureType);
283  520965 if (atts == null)
284    {
285  43 atts = new TreeMap<>(comparator);
286  43 attributes.put(featureType, atts);
287    }
288  520965 AttributeData attData = atts.get(attName);
289  520965 if (attData == null)
290    {
291  180 attData = new AttributeData();
292  180 atts.put(attName, attData);
293    }
294  520965 attData.addInstance(description, valueAsString);
295    }
296   
297    /**
298    * Answers the description of the given attribute for the given feature type,
299    * if known and unique, else null
300    *
301    * @param featureType
302    * @param attName
303    * @return
304    */
 
305  3 toggle public String getDescription(String featureType, String... attName)
306    {
307  3 String desc = null;
308  3 Map<String[], AttributeData> atts = attributes.get(featureType);
309  3 if (atts != null)
310    {
311  2 AttributeData attData = atts.get(attName);
312  2 if (attData != null)
313    {
314  2 desc = attData.getDescription();
315    }
316    }
317  3 return desc;
318    }
319   
320    /**
321    * Answers the [min, max] value range of the given attribute for the given
322    * feature type, if known, else null. Attributes with a mixture of text and
323    * numeric values are considered text (do not return a min-max range).
324    *
325    * @param featureType
326    * @param attName
327    * @return
328    */
 
329  7 toggle public float[] getMinMax(String featureType, String... attName)
330    {
331  7 Map<String[], AttributeData> atts = attributes.get(featureType);
332  7 if (atts != null)
333    {
334  6 AttributeData attData = atts.get(attName);
335  6 if (attData != null && attData.hasValue)
336    {
337  4 return new float[] { attData.min, attData.max };
338    }
339    }
340  3 return null;
341    }
342   
343    /**
344    * Records the given attribute description for the given feature type
345    *
346    * @param featureType
347    * @param attName
348    * @param description
349    */
 
350  2 toggle public void addDescription(String featureType, String description,
351    String... attName)
352    {
353  2 if (featureType == null || attName == null)
354    {
355  0 return;
356    }
357   
358  2 Map<String[], AttributeData> atts = attributes.get(featureType);
359  2 if (atts == null)
360    {
361  1 atts = new TreeMap<>(comparator);
362  1 attributes.put(featureType, atts);
363    }
364  2 AttributeData attData = atts.get(attName);
365  2 if (attData == null)
366    {
367  1 attData = new AttributeData();
368  1 atts.put(attName, attData);
369    }
370  2 attData.addDescription(description);
371    }
372   
373    /**
374    * Answers the datatype of the feature, which is one of Character, Number or
375    * Mixed (or null if not known), as discovered from values recorded.
376    *
377    * @param featureType
378    * @param attName
379    * @return
380    */
 
381  4 toggle public Datatype getDatatype(String featureType, String... attName)
382    {
383  4 Map<String[], AttributeData> atts = attributes.get(featureType);
384  4 if (atts != null)
385    {
386  3 AttributeData attData = atts.get(attName);
387  3 if (attData != null)
388    {
389  3 return attData.getType();
390    }
391    }
392  1 return null;
393    }
394   
395    /**
396    * Resets all attribute metadata
397    */
 
398  1 toggle public void clear()
399    {
400  1 attributes.clear();
401    }
402   
403    /**
404    * Resets attribute metadata for one feature type
405    *
406    * @param featureType
407    */
 
408  0 toggle public void clear(String featureType)
409    {
410  0 Map<String[], AttributeData> map = attributes.get(featureType);
411  0 if (map != null)
412    {
413  0 map.clear();
414    }
415   
416    }
417    }